前一段时间下班看了不少JVM相关资料。总结分享一下,希望对看到的人有所帮助。
大部分整理自网络,同时也可参考我的JVM分类blog:http://shensy.iteye.com/category/211309
----------------------------------------------
一、基本概念
1、现在的JVM垃圾收集主要使用分代回收算法:即把对象分为青年代,老年代,持久代,处于不同代中的对象采用不同的垃圾回收算法,例如:复制,标记整理,增量收集。
2、JVM堆中的分代:
(1)年轻代:
分为一个eden区和2个surivor区,大部分对象在eden区中生成,当eden区满时,还存活的对象将被复制到一个surivor区(surivor总有一个是空的),当这个surivor满时,里面存活的对象将被复制到另外一个surivor(同时清空第一个surivor中的垃圾对象),当那个surivor区也满了时,第一个surivor复制过来还存活的对象将被复制到年老区(同一个区可能同时存在从eden复制过来和从前一个surivor复制过来的对象,但复制到年老区的只有从前一个surivor过来的对象)。
(2)年老代:
一般来说年老代存放的是生命期较长的对象。
(3)持久代:
用于存放静态文件,持久代对垃圾回收没有显著影响,但有些应用可能动态生成或者调用一些class,这时需要设置一个比较大的持久代空间来存放这些运行中新增的类,持久代通过-XX:MaxPermSize=进行设置。
3、GC类型:
(1)Scavenge GC(Young GC):
年轻代Eden区申请空间失败时,触发YOUNG GC.在堆Eden区域执行GC,清除非存活对象.把存活对象放至Survivor区,然后整理2个Survivor区.
(2)Full GC:
对整个堆进行整理,包括年轻代,年老代,永久代。FullGC比YoungGC要慢,导致FullGC原因是:
年老代被写满、永久代被写满、System.gc()被调用、上一次GC之后Heap的各域分配策略动态变化。
4、垃圾回收器:
串行收集器:应用停止,单线程进行所有回收工作。适用于单处理器的机器。
并行收集器:应用停止,多线程进行并发垃圾回收,对年轻代和年老代进行并行垃圾回收(可通过参数配置),但标记-清除为单线程。
并发收集器:大部分工作都并发进行(垃圾回收和标记-清除),垃圾回收只暂停很少的时间。在应用不停止的情况下使用独立的垃圾回收线程。即并发收集是在应用运行时进行收集。
垃圾回收起点:
垃圾回收的起点是一些根对象(java栈, 静态变量, 寄存器...)。
栈是真正进行程序执行地方,所以要获取哪些对象正在被使用,则需要从Java栈开始。同时,一个栈是与一个线程对应的,因此,如果有多个线程的话,则必须对这些线程对应的所有的栈进行检查。
基本垃圾回收算法:http://pengjiaheng.iteye.com/blog/520228
三种垃圾收集器详解:http://pengjiaheng.iteye.com/blog/528034
5、JVM结构:
程序计数器:当前线程所执行的字节码的行号指示器。每个线程独有一个计数器。该部分区域是JVM规范中唯一没有规定任何OOM Error情况的区域。
虚拟机栈:存储基本数据类型、对象引用、方法出口、局部变量,也是线程私有的。
本地方法栈:执行Native方法。
堆:存储对象,各线程共享。详情见堆介绍。
方法区:各线程共享内存、用于存储加载的类信息、常量、静态变量。
常量池:是方法区的一部分,存储编译期生成的字面量。运行期也可能将新的常量放入池中,例如:string.intern()方法。
直接内存:NIO引入基于Channel和Buffer的IO方式,可以使用Native函数库直接分配堆外内存。然后通过Java堆里面的DirectByteBuffer对象作为内存引用进行操作。
其中:
栈是运行时单位,堆是存储单位。
每个线程都有一个线程栈,该线程独享该栈内空间,存储局部变量、程序运行状态、方法返回值等。
堆是线程内共享,只存储对象。
Java中main就是栈的起始点也是程序起始点。
Java方法调用参数都是传值,传引用可以理解为传引用值。
堆和栈中,栈是程序运行最根本的东西。程序运行可以没有堆,但是不能没有栈。
堆是为栈进行数据存储服务,就是一块共享的内存。
对象引用类型:
分为强引用、软引用、弱引用和虚引用。
强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收。
软引用一般被做为缓存来使用。与强引用的区别是,软引用在垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。
弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在进行垃圾回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。
解决内存碎片问题:
在程序运行一段时间以后,如果不进行内存整理,就会出现零散的内存碎片。碎片最直接的问题就是会导致无法分配大块的内存空间,以及程序运行效率降低。
基本垃圾回收算法中,“复制”方式和“标记-整理”方式,都可以解决碎片的问题。
解决应用暂停问题:
并发垃圾回收算法,使用这种算法,垃圾回收线程与程序运行线程同时运行。在这种方式下,解决了暂停的问题,但是因为需要在新生成对象的同时又要回收对象,算法复杂性会大大增加,系统的处理能力也会相应降低,同时,“碎片”问题将会比较难解决。
二、JVM参数调优示例:
本示例是公司某服务器在高并发生产环境正在使用的JVM启动参数。
使用的Java版本信息:java -version
java version "1.6.0_30" Java(TM) SE Runtime Environment (build 1.6.0_30-b12) Java HotSpot(TM) 64-Bit Server VM (build 20.5-b03, mixed mode)
优化参数如下:
nohup java -Xms4g -Xmx4g -Xmn3g -Xss256k \ -server \ -XX:PermSize=64M \ -XX:MaxPermSize=64M \ -XX:+UseConcMarkSweepGC \ -XX:+UseAdaptiveSizePolicy \ -XX:+CMSClassUnloadingEnabled \ -XX:+UseCMSCompactAtFullCollection \ -XX:+DisableExplicitGC \ -XX:CMSFullGCsBeforeCompaction=10 \ -XX:CMSMaxAbortablePrecleanTime=5 \ -XX:+HeapDumpOnOutOfMemoryError \ -jar server.jar 2>&1 >/dev/null &
逐个参数进行分析:
-Xms4g:初始堆大小4g。
-Xmx4g:最大堆大小4g。
-Xmn3g:设置年轻代大小3g。
整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss256k:设置每个线程的堆栈大小256k。
JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-server:jvm以server模式启动。
JVM Server模式与client模式启动,最主要的差别在于:-Server模式启动时,启动速度较慢(比client模式慢大概10%),但是一旦运行起来后,性能将会有很大的提升。JVM如果不显式指定是-Server模式还是-client模式,JVM能够进行自动判断(J2SE5.0检测的根据是至少2个CPU和最低2GB内存)。
通过运行:java -version来查看jvm默认工作在什么模式。
-XX:PermSize=64M:设置永久代(方法区)初始大小
在一个jvm实例的内部,类型信息被存储在一个称为方法区的内存逻辑区中。类型信息是由类加载器在类加载时从类文件中提取出来的。类(静态)变量也存储在方法区中。
-XX:MaxPermSize=64M 设置方法区最大值。
-XX:+UseConcMarkSweepGC:设置年老代为并发收集。
并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。
-XX:+UseAdaptiveSizePolicy:并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开。(-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。)
-XX:+CMSClassUnloadingEnabled:设置对Perm区进行回收。
如果使用Spring/Hibernate框架大量采用cglib,导致生成的Proxy会比较多,而这些是存放在PermGen区域,SUN JDK默认情况下不会去回收,必须加上-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled参数,JDK才会去回收这部分数据。
使用并发收集器CMS(ConcMarkSweep)策略时,必须有:-XX:+CMSPermGenSweepingEnabled和-XX:+CMSClassUnloadingEnabled来配合同时启用,才可以对PermGen进行GC(
实际主要参数为:-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
)。只是参数:-XX:+CMSPermGenSweepingEnabled在JDK1.6中是不需要的。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。
可能会影响性能,但是可以消除内存碎片。
-XX:CMSFullGCsBeforeCompaction=10:打开年老代压缩的情况下,设置10次Full GC后,对年老代进行压缩。
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。
-XX:+DisableExplicitGC:关闭System.gc()
System.gc()强迫VM执行一个堆的“全部清扫”,全部清扫比一个常规GC操作要昂贵好几个数量级。该参数将System.gc()调用转换成一个空操作.
-XX:CMSMaxAbortablePrecleanTime=5:调小CMSMaxAbortablePrecleanTime的值=5
CMS GC须要经过较多步骤才能完成一次GC的动作,在minor GC较为频繁的情况下,很有可能造成CMS GC尚未完成,从而造成concurrent mode failure,这种情况下,降低minor GC触发的频率是一种要领,另外一种要领则是加快CMS GC执行时间,在CMS的整个步骤中,JDK 5.0+、6.0+的有些版本在CMS-concurrent-abortable-preclean-start和CMS-concurrent-abortable-preclean这两步间有可能会耗费很长的时间,导致可回收的旧生代的对象很长时间后才被回收,这是Sun JDK CMS GC的一个bug[1],如通过PrintGCDetails观察到这两步之间耗费了较长的时间,可以通过-XX: CMSMaxAbortablePrecleanTime配置较小的值,以保证CMS GC尽快完成对象的回收,防止concurrent mode failure的现象。
-XX:+HeapDumpOnOutOfMemoryError:
抛出OutOfMemoryError时,该命令通知JVM拍摄一个“堆转储快照”,并将其保存在一个文件中以便处理,通常使用jhat工具。可以使用相应的-XX:HeapDumpPath标志指定到保存文件的实际路径(确保文件系统和必须要有权限可以在其中写入)。
其它相关命令:
nohup和最后的&:代表以后台服务方式运行,\代表shell中的换行.
-jar server.jar:执行jar包。
关于2>&1 >/dev/null:
shell中0>表示stdin标准输入;1>表示stdout标准输出;2>表示stderr错误输出;
&可以理解为是"等同于"的意思,2>&1,即表示2的输出重定向等同于1
符号> 等价于1>(系统默认为1,省略了先);所以">/dev/null"等同于"1>/dev/null"
/dev/null代表空设备文件。
该语句解释为:stderr错误输出与stdout标准输出都重定向到空设备文件,也就是不输出任何信息到终端,就是不显示任何信息。
相关推荐
在Java开发领域,JVM(Java Virtual Machine)是运行所有Java应用程序的核心,它负责解析字节码..."JVM调优示例2"为我们提供了一个实践的场景,通过学习和实践,我们可以提升Java应用的性能,打造更加高效稳定的系统。
本文将详细解析JVM参数调优、垃圾回收(GC)算法及其原理,以帮助优化系统性能。 首先,对于JVM参数调优,有以下八条重要的建议: 1. 选择64位操作系统,尽管64位JDK在Linux上运行可能稍慢,但它能支持更大的内存...
在Java开发领域,JVM(Java Virtual Machine)是运行所有Java应用程序的...对于"jvm调优示例"中的程序,可以通过分析其运行情况,配合JVM提供的工具,如VisualVM或JProfiler,来定位和解决问题,实现更高效的运行效果。
### 马士兵JVM调优笔记知识点梳理 #### 一、Java内存结构 Java程序运行时,其内存被划分为几个不同的区域,包括堆内存(Heap)、方法区(Method Area)、栈(Stack)、程序计数器(Program Counter Register)以及...
### 如何配置JVM参数,实现高效调优 在IT行业,Java虚拟机(JVM)作为执行Java程序的核心环境,其性能优化是确保应用程序稳定、高效运行的关键。本文将深入探讨如何合理配置JVM参数,以达到最优的系统表现。 #### ...
### JVM工具详解与参数调优&调试技巧 #### 一、JVM工具 JVM工具是一系列用于监控、管理和诊断Java虚拟机运行状态的工具集合。这些工具可以帮助开发者更好地理解和优化Java应用程序的性能。 ##### 1. jps:虚拟机...
在对Java虚拟机(JVM)进行调优的过程中,我们首先...总体而言,JVM调优是一个系统化的过程,它需要对JVM的工作机制有深入的理解,并且结合实际应用进行测试和调优。有效的JVM调优能够显著提升Java应用的性能和稳定性。
JVM性能调优是一个多方面的任务,涉及JVM配置、垃圾回收策略、内存管理、线程使用等多个方面。通过采用合适的调优技巧和工具,开发者可以显著提高Java应用程序的性能和响应速度。然而,性能调优应基于实际的性能瓶颈...
#### 具体调优示例 1. **调整新生代和老年代比例**:可以通过设置`-XX:NewRatio`参数来改变新生代和老年代之间的比例。例如,设置`-XX:NewRatio=2`意味着新生代与老年代的比例为1:2。 2. **选择合适的垃圾回收算法...
在Java开发领域,JVM(Java ...以上就是JVM调优的一些基本概念和关键点,"JVM-master"项目可能包含了实践这些概念的示例和教程,通过学习和实践,我们可以深入理解JVM的工作原理,从而更好地优化我们的Java应用。
# 基于Java虚拟机(JVM)的内存管理与垃圾回收系统 ## 项目简介 本项目深入探讨了Java虚拟机(JVM)的内存...4. JVM调优提供了JVM调优的实战指南,包括如何选择合适的垃圾收集器、调整堆内存大小、设置GC停顿时间等。
调优示例往往涉及到监控JVM运行时的状态,比如内存使用、垃圾回收次数、线程状态等,然后根据数据进行分析和参数调整。 JVM堆空间布局分为年轻代和老年代,年轻代又细分为Eden区、Survivor区。年轻代用于存放生命...
本文主要围绕四个核心JVM参数:-Xms、-Xmx、-Xmn和-Xss进行深入讲解,并结合实际配置示例来阐述其作用和调优策略。 1. `-Xms` 和 `-Xmx`: 这两个参数用于设定JVM堆内存的最小和最大值。例如,`-Xms3550m`表示初始...
JVM调优是一个复杂的过程,涉及到多个方面的参数调整。针对不同的应用场景和问题,需要灵活运用各种参数和技术手段来优化JVM性能。通过对JVM参数的理解和合理配置,可以显著提升Java应用的运行效率和稳定性。希望...
为了优化垃圾回收(GC)效率,JVM将内存分为新生代(Young Generation)、老年代(Tenured Generation)和持久代(Permanent Generation或元空间,Java 8后改为元空间MetaSpace)。新生代主要存放新创建的对象,...
### Tomcat-JVM调优详解 #### 一、概述 在服务器端应用开发与部署过程中,JVM(Java虚拟机)的性能优化是一项至关重要的任务。对于基于Java的应用程序而言,Tomcat作为最常见的Web容器之一,其运行时的性能表现...
本文将对JVM调优中的几个关键参数进行深入解析,包括-Xms、-Xmx、-Xmn和-Xss等,帮助开发者更好地理解这些参数的作用及如何合理设置。 #### 1. -Xms(Initial Heap Size) - **定义**:设置JVM启动时初始分配给堆...
在进行JVM调优时,我们需要考虑对象的分配策略、垃圾回收器的选择、内存大小设置等多个方面,以确保应用程序高效、稳定地运行。此外,了解JVM内存模型的划分,如年轻代、老年代和持久代,以及不同代之间的晋升策略,...
- `-XX:PermSize` 和 `-XX:MaxPermSize` 用于设置永久代的初始和最大大小,这两个参数在较新的JVM版本(如Java 8及以上)中已被移除,取而代之的是元空间(Metaspace)。 3. **垃圾收集器配置**: - `-XX:+...