jvm调优

上学期把周志明先生的《深入理解java虚拟机》读了一遍,仅仅停留在看过层面上,却一点没有动手尝试,于是准备从jvm调优上面着手。第二三四章节分别介绍了java内存结构、gc、jvm性能监控几个方面,看过之后对这几个常用的jvm监控工具比较感兴趣于是翻了翻别人的文章学习了一下。对照书章节的结构总结一下:


目录:


<!----相关基础知识---->

1.jvm内存区域

2.jvm垃圾回收区域与算法

3.内存分配与回收策略

4.gc日志以及日志相关参数

5.jvm内存溢出


<!----调优分析---->

6.jvm相关参数

7.jdk自带的调试工具

8.调优策略


<!----分割线---->


1.jvm内存区域


java虚拟机在执行程序过程中会将内存区分为几个运行时数据区域,根据《java虚拟机规范SE7版》如。

jvm运行时数据区域图


程序计数器
是一块较小的内存空间,其作用可以看作是当前线程所执行的字节码的行号指示器,字节码解析器工作时 通过改变程序计数器的值来选取下一条需要执行的字节码指令。程序的分支、循环、跳转、异常处理以及线程恢复等基础功能都是依赖程序计数器来完成。
Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间片来实现,在任何一个时刻,一个处理器只会执行一条线程指令,因此, 为了确保线程切换之后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,因此程序计数器是线程私有的内存。程序计数器是java虚拟机中唯一一个 没有规定任何内存溢出OutOfMemoryError的内存区域。

Java虚拟机栈
Java虚拟机栈也是 线程私有的,它的生命周期与线程相同。虚拟机栈描述的是java方法执行的内存模型: 每个方法被执行时都会同时创建一个栈帧用于存放局部变量表、操作数栈、动态连接和方法出口等信息。Java虚拟机栈的 局部变量表存放了编译器可知的8种java基本类型数据、对象引用(注意不是对象实例本身)、方法返回地址returnAddress
Java虚拟机栈有两种异常状况: 如果线程请求的栈深度大于虚拟机所允许的最大深度时,抛出StackOverflowError异常;如果虚拟机栈可以动态扩展, 当扩展时无法申请到足够内存时会抛出OutOfMemoryError异常

本地方法栈
本地方法栈与java虚拟机栈作用非常类似,其区别是:java虚拟机栈是为虚拟机执行java方法服务,而本地方法栈是为虚拟机调用的操作系统本地方法服务。Java虚拟机规范没有对本地方法栈的实现和数据结构做强制规定,Sun HotSpot虚拟机直接把java虚拟机栈和本地方法栈合二为一。与java虚拟机栈类似,本地方法栈也会抛出StackOverflowError异常和OutOfMemoryError异常。

Java堆
堆是java虚拟机所管理的内存区域中最大一块,java堆是被 所有线程所共享的一块内存区域,在java虚拟机启动时创建,堆内存的唯一目的就是 存放对象实例。几乎所有的对象实例都是在堆分配内存。
Java堆是 垃圾收集器管理的主要区域,从垃圾回收的角度看,由于现在的垃圾收集器基本都采用的是分代收集算法,因此java 堆还可以初步细分为新生代和年老代
Java虚拟机规范规定,堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。在实现上即可以是固定大小的,也可以是可动态扩展的。 如果在堆中没有内存完成实例分配,并且堆大小也无法在扩展时,将会抛出OutOfMemoryError异常

方法区
方法区与堆一样,是被 各个线程共享的内存区域,它用于 存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。虽然java虚拟机规范把方法区描述为堆的一个逻辑部分,但是方法区却有一个别名叫Non-Heap(非堆)。
Sun HotSpot虚拟机把方法区叫 永久代(Permanent Generation),方法区中最重要的部分是 运行时常量池。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面变量、符号引用、直接引用等,这些内容将在类加载后存放到方法区的运行时常量池中,另外在运行期间也可以将新的常量存放到常量池中,如String的intern()方法。方法区和运行时常量池在无法满足内存分配时,也会抛出OutOfMemoryError异常。

直接内存
直接内存并不是java虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,但是在java开发中还是会使用到。JDK1.4中新引入的NIO(new I/O),引入了一种基于通道(Channel)和缓冲区(Buffer)的I/O方式,可以使用操作系统本地方法库直接分配堆外内存,然后通过一个存储在java堆里面的DirectByteBuffer对象作为堆外直接内存的引用进行操作,避免了java堆内存和本地直接内存间的数据拷贝,可以显著提高性能。虽然直接内存并不直接收到java虚拟机内存影响,但是如果java虚拟机各个内存区域总和大于物理内存限制,从而导致直接内存不足,动态扩展时也会抛出OutOfMemoryError异常。

2.jvm垃圾回收区域与算法


首先垃圾回收要确定三件事情:哪些内存需要回收?什么时候回收?如何回收?

非重点gc区域
程序计数器虚拟机栈本地方法栈3个区域随线程而生,随线程而灭,线程结束,内存回收。栈中的栈帧随着方法的进入和退出而有条不紊地执行着入栈和出栈操作。方法结束,内存回收。

重点gc区域
Java堆,只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收是动态的。
同时 方法区,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,这部分内存的分配和回收是动态的。

堆---对象是否可回收
1) 引用计数算法
给对象添加一个引用计数器,每当有一个地方引用它的地方,计数器值+1;当引用失效,计数器值就减1;任何时候计数器为0,对象就不可能再被引用了。
它很难解决对象之间相互循环引用的问题。
2) 可达性分析算法
通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
在Java语言中,可以作为Gc Roots的对象包括下面几种:
虚拟机栈(栈帧中的本地变量表)中引用的对象。
方法区中类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI(即一般说的Native方法)引用的对象。
3) 引用分类
在JDK 1.2 之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱。
4) 对象死亡判定
真正宣告一个对象死亡,至少要经历 两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被 第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行 第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。下面例子可以看出finalize()被执行,但是它仍然可以存活。

方法区---废弃常量和无用的类
永久代的垃圾回收主要回收两部分内容:废弃常量和无用的类。“废弃常量”判断比较简单,但是“无用的类”的判断复杂一些,需要满足下面3个条件:
该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
加载该类的ClassLoader已经被回收。
该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

垃圾回收算法
1) 标记-清除算法
如同它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它的主要不足有两个:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
2) 复制算法
为了解决效率问题,一种称为“复制”(Copying)的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半,未免太高了一点。
3) 标记-整理算法
标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
4) 分代收集算法
根据对象存活周期的不同将内存分为几块。一般把Java堆分为新生代和老年代,根据各个年代的特点采用最合适的收集算法。在新生代中,每次垃圾收集时有大批对象死去,只有少量存活,可以选用复制算法。而老年代对象存活率高,使用标记清除或者标记整理算法。

3.内存分配与回收策略


内存分配过程分析是jvm调优重要的一块知识,包括:
对象优先在Eden分配,大对象直接进入老年代,长期存活的对象将进入老年代,空间分配担保。

对象优先在Eden分配
首先介绍两种GC:
新生代GC,也叫Minor GC,指发生在新生代的垃圾回收动作,由于JAVA的大多数对象具有朝生夕灭的特性,因此这种GC发生的很 频繁,回收速度也较快。
老年代GC,也叫Full GC或者Major GC,指发生在老年代的垃圾收集动作,经常伴随至少一次的Minor GC,但也不是绝对的, 速度一般比Minor GC慢10倍以上

当Eden区没有足够的空间分配时,虚拟机会发起一次Minor GC,这里我们要知道的是 新生区包括Eden区和一个Survivor区,但是是存在2个Survivor区的,2个Suivivor中必定有一个区是空的。因此新生代总的可用空间就是(Eden+1个Survivor)。如果分配时发现Eden区没有足够的内存,那么会发生一次Minor GC,这期间发生的动作会导致Eden区的对象进入一个Survivor区,如果虚拟机发现Eden区的对象太大以至于无法全部放入1个Survivor区,会通过分配担保机制提前将Eden区内的对象转移到老年代去

大对象直接进入老年代
因为新生代采用复制算法收集内存,因此大对象对于JVM是很苦恼的,尤其是当这些大对象很短命的时候,经常出现大对象容易导致内存还有不少空间时就提前出发垃圾收集以获取足够的连续空间来安置他们。
因此,当产生大对象时,会将其直接分配在老年代。

长期存活的对象将进入老年代
虚拟机管理内存的思想是分代收集。每个对象定义了一个对象年龄计数器, 当对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor中,并且其年龄被置为1,每度过一次GC(即每次从一个Survivor转移到另一个Survivor中),年龄增加1,当到达15岁时,晋升到老年代。15这个年龄值是可以人为改变的。但是并不是一定要到达年龄阀值时对象才会被转移到老年代,JVM中存在动态对象年龄判定来适应不同程序的内存状况。如果在Survivor空间中相同年龄的所有对象的大小总和大于Survivor空间的一半,这些对象可以提前直接进入老年代。

空间分配担保
在发生Minor GC之前,JVM会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间(防止在经过这次Minor GC之后存活的新生代的所有对象年龄达到阀值或者满足动态对象年龄判定条件,导致进入老年代时,老年代空间不够),如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,会继续检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,尝试进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,那么不允许冒险,此时就需要进行一次Full GC来清理老年代。
上文提到的冒险:新生代采用复制收集算法,但是为了内存利用率,只是用一个Survivor作为轮换备份,Minor GC后最极端的情况是新生代的所有对象都存活,这时Eden区的对象要进入Survivor区中,老年代要进行分配担保来把Survivor无法容纳的对象直接进入老年代。因为经过Minor GC后到底有多少对象存活在完成内存回收之前是未知的,因此取之前每次晋升到老年代对象的容量的平均大小作为经验值与老年代剩余空间进行比较来决定是否进行Full GC来让老年代腾出更多空间。

4.gc日志以及gc器参数


gc日志样例
33.125: [GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs]
100.667: [Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->210K(19456K), [Perm : 2999K->2999K(21248K)], 0.0150007 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]

日志解释
1)最前面的数字“33.125:”和“100.667:”代表了GC发生的时间,这个数字的含义是从Java虚拟机启动以来经过的秒数。
2)GC日志开头的“[GC”和“[Full GC”说明了这次垃圾收集的停顿类型,而不是用来区分新生代GC还是老年代GC的。如果有“Full”,说明这次GC是发生了Stop-The-World的。如果是调用System.gc()方法所触发的收集,那么在这里将显示“[Full GC (System)”。
3)[DefNew”、“[Tenured”、“[Perm”表示GC发生的区域
4)方括号内部的“3324K->152K(3712K)”含义是“GC前该内存区域已使用容量-> GC后该内存区域已使用容量 (该内存区域总容量)”。而在方括号之外的“3324K->152K(11904K)”表示“GC前Java堆已使用容量 -> GC后Java堆已使用容量 (Java堆总容量)”。
5)“0.0025925 secs”表示该内存区域GC所占用的时间,单位是秒。

gc器参数
参数 描述
-XX:+UseSerialGC 虚拟机运行在Client模式下的默认值,打开此开关后,使用Serial+Serial Old的收集器组合进行内存回收
UseParNewGC 打开此开关后,使用ParNew+SerialOld的收集器组合进行内存回收
UseConcMarkSweepGC 打开此开关后,使用ParNew+CMS+Serial Old的收集器组合进行内存回收。Serial Old收集器将作为CMS收集器出现Concurrent Mode Failure失败后的后备收集器使用
UseParallelGC 虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge+Serial Old(PS MarkSweep)的收集器组合进行内存回收
UseParallelOldGC 打开此开关后,使用Parallel Scavenge+Parallel Old的收集器组合进行内存回收
SurvivorRatio 新生代中Eden区与Survivor区的容量比值,默认为8,代表Eden:Survivor=8:1
PretenureSizeThreshold 直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配
MaxTenuringThreshold 晋升到老年代的对象年龄。每个对象在坚持过一次Minor GC之后,年龄就增加1,当超过这个参数值时就进入老年代
UseAdaptiveSizePolicy 动态调整java堆中各个区域的大小以及进入老年代的年龄
HandlePromotionFailure 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survivor区的所有对象都存活的极端情况
ParallelGCThreads 设置并行GC时进行内存回收的线程数
GCTimeRatio GC时间占总时间的比率,默认值为99,即允许1%的GC时间,尽在使用Parallel Scavenge收集器时生效
MaxGCPauseMillis 设置GC的最大停顿时间。仅在使用Parallel Scavenge收集器时生效
CMSInitiatingOccupancyFraction 设置CMS收集器在老年代空间被使用多少后触发垃圾收集。默认值为68%,仅在使用CMS收集器时生效
UseCMSCompactAtFullCollection 设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理。尽在使用CMS收集器时生效
CMSFullFCsBeforeCompaction 设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS收集器时生效

5.jvm内存溢出


按照异常类型
1)java.lang.OutOfMemoryError: Java heap space —-JVM Heap(堆)溢出:
JVM 在启动的时候会自动设置 JVM Heap 的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存。可以利用 JVM提供的 -Xmn -Xms -Xmx 等选项可进行设置。Heap 的大小是 Young Generation 和 Tenured Generaion 之和。在 JVM 中如果 98% 的时间是用于 GC,且可用的 Heap size 不足 2% 的时候将抛出此异常信息。
2)java.lang.OutOfMemoryError: PermGen space —- PermGen space溢出:
PermGen space 的全称是 Permanent Generation space,是指内存的永久保存区域。为什么会内存溢出,这是由于这块内存主要是被 JVM 存放Class 和 Meta 信息的,Class 在被 Load 的时候被放入 PermGen space 区域,它和存放 Instance 的 Heap 区域不同,sun 的 GC 不会在主程序运行期对 PermGen space 进行清理,所以如果你的 APP 会载入很多 CLASS 的话,就很可能出现 PermGen space 溢出。
3)java.lang.StackOverflowError —- 栈溢出:
栈溢出了,JVM 依然是采用栈式的虚拟机,这个和 C 与 Pascal 都是一样的。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K 的空间(这个大约相当于在一个 C 函数内声明了 256 个 int 类型的变量),那么栈区也不过是需要 1MB 的空间。通常栈的大小是 1-2MB 的。
通常递归也不要递归的层次过多,很容易溢出。

按照内存区域
1)Java虚拟机栈:
如果线程请求的栈深度大于虚拟机所允许的最大深度时,抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,当扩展时无法申请到足够内存时会抛出OutOfMemoryError异常。
2)本地方法栈:
与java虚拟机栈类似,本地方法栈也会抛出StackOverflowError异常和OutOfMemoryError异常。
3)Java堆:
如果在堆中没有内存完成实例分配,并且堆大小也无法在扩展时,将会抛出OutOfMemoryError异常。
4)方法区:
方法区和运行时常量池在无法满足内存分配时,也会抛出OutOfMemoryError异常。
5)直接内存:
虽然直接内存并不直接收到java虚拟机内存影响,但是如果java虚拟机各个内存区域总和大于物理内存限制,从而导致直接内存不足,动态扩展时也会抛出OutOfMemoryError异常。

6.jvm相关参数


常用参数

参数名称 含义 默认值 说明
-Xms 初始堆大小 物理内存的1/64(<1GB) 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.
-Xmx 最大堆大小 物理内存的1/4(<1GB) 默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制
-Xmn 年轻代大小(1.4or lator)
注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小.增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize 设置年轻代大小(for 1.3/1.4)

-XX:MaxNewSize 年轻代最大值(for 1.3/1.4)

-XX:PermSize 设置持久代(perm gen)初始值 物理内存的1/64
-XX:MaxPermSize 设置持久代最大值 物理内存的1/4
-Xss 每个线程的堆栈大小
JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右.一般小的应用, 如果栈不是很深, 应该是128k够用的.大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。
-XX:ThreadStackSize Thread Stack Size
(0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]
-XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)
-XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden区与Survivor区的大小比值
设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:LargePageSizeInBytes 内存页的大小不可设置过大, 会影响Perm的大小
=128m
-XX:+UseFastAccessorMethods 原始类型的快速优化

-XX:+DisableExplicitGC 关闭System.gc()
这个参数需要严格的测试
-XX:MaxTenuringThreshold 垃圾最大年龄
如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代.对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率.该参数只有在串行GC时才有效.
-XX:+AggressiveOpts 加快编译

-XX:+UseBiasedLocking 锁机制的性能改善

-Xnoclassgc 禁用垃圾回收

-XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中SoftReference的存活时间 1s softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap
-XX:PretenureSizeThreshold 对象超过多大是直接在旧生代分配 0 单位字节 新生代采用Parallel Scavenge GC时无效,另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象.
-XX:TLABWasteTargetPercent TLAB占eden区的百分比 1%
-XX:+CollectGen0First FullGC时是否先YGC false

并行收集器相关参数

参数名称 含义 默认值 说明
-XX:+UseParallelGC Full GC采用parallel MSC(此项待验证)
选择垃圾收集器为并行收集器.此配置仅对年轻代有效.即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集.(此项待验证)
-XX:+UseParNewGC 设置年轻代为并行收集
可与CMS收集同时使用,JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值
-XX:ParallelGCThreads 并行收集器的线程数
此值最好配置与处理器数目相等 同样适用于CMS
-XX:+UseParallelOldGC 年老代垃圾收集方式为并行收集(Parallel Compacting)
这个是JAVA 6出现的参数选项
-XX:MaxGCPauseMillis 每次年轻代垃圾回收的最长时间(最大暂停时间)
如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值.
-XX:+UseAdaptiveSizePolicy 自动选择年轻代区大小和相应的Survivor区比例
设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开.
-XX:GCTimeRatio 设置垃圾回收时间占程序运行时间的百分比
公式为1/(1+n)
-XX:+ScavengeBeforeFullGC Full GC前调用YGC true Do young generation GC prior to a full GC. (Introduced in 1.4.1.)

CMS相关参数

参数名称 含义 默认值 说明
-XX:+UseConcMarkSweepGC 使用CMS内存收集
测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明.所以,此时年轻代大小最好用-Xmn设置
-XX:+AggressiveHeap

试图是使用大量的物理内存长时间大内存。使用的优化,能检查计算资源(内存, 处理器数量)至少需要256MB内存,大量的CPU/内存, (在1.4.1在4CPU的机器上已经显示有提升)
-XX:CMSFullGCsBeforeCompaction 多少次后进行内存压缩
由于并发收集器不对内存空间进行压缩,整理,所以运行一段时间以后会产生”碎片”,使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩,整理.
-XX:+CMSParallelRemarkEnabled 降低标记停顿

-XX+UseCMSCompactAtFullCollection 在FULL GC的时候, 对年老代的压缩
CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片
-XX:+UseCMSInitiatingOccupancyOnly 使用手动定义初始化定义开始CMS收集
禁止hostspot自行触发CMS GC
-XX:CMSInitiatingOccupancyFraction=70 使用cms作为垃圾回收,使用70%后开始CMS收集 92 为了保证不出现promotion failed(见下面介绍)错误,该值的设置需要满足以下公式CMSInitiatingOccupancyFraction计算公式
-XX:CMSInitiatingPermOccupancyFraction 设置Perm Gen使用到达多少比率时触发 92
-XX:+CMSIncrementalMode 设置为增量模式
用于单CPU情况
-XX:+CMSClassUnloadingEnabled


辅助信息

参数名称 含义 默认值 说明
-XX:+PrintGC

输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs][Full GC 121376K->10414K(130112K), 0.0650971 secs]
-XX:+PrintGCDetails

输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs]118250K->113543K(130112K), 0.0124633 secs][GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs]121376K->10414K(130112K), 0.0436268 secs]
-XX:+PrintGCTimeStamps


-XX:+PrintGC:PrintGCTimeStamps

可与-XX:+PrintGC -XX:+PrintGCDetails混合使用。输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间.可与上面混合使用
输出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间.可与上面混合使用
输出形式:Application time: 0.5291524 seconds
-XX:+PrintHeapAtGC 打印GC前后的详细堆栈信息

-Xloggc:filename 把相关日志信息记录到文件以便分析.与上面几个配合使用

-XX:+PrintClassHistogram garbage collects before printing the histogram.

-XX:+PrintTLAB 查看TLAB空间的使用情况

XX:+PrintTenuringDistribution 查看每次minor GC后新的存活周期的阈值
Desired survivor size 1048576 bytes, new threshold 7 (max 15)

7.jdk自带的调试工具


了解了jvm的gc和内存管理、jvm相关内存参数、jvm相关gc参数之后,使用jdk自带的调试工具就可以监控jvm运行状态、性能分析、参数修改等。jdk自带下面几种工具:
jps 虚拟机进程相关工具
jstat 虚拟机各个方面的运行参数搜集工具
jinfo 虚拟机参数配置工具
jmap 生成虚拟机的内存转储快照
jhat 分析heapdump文件,用户可通过浏览器查看 
jstack 显示虚拟机线程快照

还有一个cpu使用率相关工具也可以生成详细的heap dump信息
hprof  hprof能够展现CPU使用率,统计堆内存使用情况


下面测试的jdk版本如下:
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b15, mixed mode)


1.jps
jps主要用来输出JVM中运行的进程状态信息。语法格式如下:
jps [options] [hostid]

如果不指定[hostid]就默认为当前主机或服务器。

[options]选项说明如下:
-q 不输出类名、Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数



2.jstat
jstat [generalOption | outputOptions vmid [interval[s|ms] [count]]]
vmid是Java虚拟机ID,在Linux/Unix系统上一般就是进程ID。interval是采样时间间隔。count是采样数目。

[options]选项说明如下:
-class
-compiler
-gc
-gccapacity
-gccause
-gcmetacapacity
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcutil
-printcompilation

输出GC信息(时间间隔500ms、10个样本):


结果各项说明:
S0C 新生代中Survivor space中S0当前容量的大小(KB)
S1C 新生代中Survivor space中S1当前容量的大小(KB)
S0U 新生代中Survivor space中S0容量使用的大小(KB)
S1U 新生代中Survivor space中S1容量使用的大小(KB)
EC Eden space当前容量的大小(KB)
EU Eden space容量使用的大小(KB)
OC Old space当前容量的大小(KB)
OU Old space使用容量的大小(KB)
PC Permanent space当前容量的大小(KB)
PU Permanent space使用容量的大小(KB)
YGC 从应用程序启动到采样时发生 Young GC 的次数
YGCT 从应用程序启动到采样时 Young GC 所用的时间(秒)
FGC 从应用程序启动到采样时发生 Full GC 的次数
FGCT 从应用程序启动到采样时 Full GC 所用的时间(秒)
GCT T从应用程序启动到采样时用于垃圾回收的总时间(单位秒),它的值等于YGC+FGC
堆内存 = 年轻代 + 年老代 + 永久代
年轻代 = Eden区 + 两个Survivor区

3.jinfo
jinfo可以输出并修改运行时的java 进程的opts。用处比较简单,用于输出JAVA系统参数及命令行参数。命令格式:
jinfo [option] pid

[options]选项说明如下:
-flag <name>  打印指定的jvm参数值
-flag [+|-]<name> 启用或者禁用指定的jvm参数
-flag <name>=<value>为指定的jvm参数设置值
-flags 打印所有的jvm参数值
-sysprops 打印系统性能
<no option> 默认输出上面所有信息

如图:


下面输出一个eclipse启动信息:
C:\Users\pc>jinfo -sysprops 14416
Attaching to process ID 14416, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b15
java.vendor = Oracle Corporation
sun.java.launcher = SUN_STANDARD
osgi.bundles.defaultStartLevel = 4
org.eclipse.debug.ui.breakpoints.toggleFactoriesUsed = true
org.osgi.supports.framework.extension = true
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
eclipse.p2.profile = epp.package.jee
os.name = Windows 10
sun.boot.class.path = D:\jre\lib\resources.jar;D:\jre\lib\rt.jar;D:\jre\lib\sunrsasign.jar;D:\jre\lib\jsse.jar;D:\jre\lib\jce.jar;D:\jre\lib\charsets.jar;D:\jre\lib\jfr.jar;D:\jre\classes
osgi.ws = win32
sun.desktop = windows
java.vm.specification.vendor = Oracle Corporation
java.runtime.version = 1.8.0_91-b15
osgi.instance.area = file:/D:/Myeclipse/workspace/
org.osgi.framework.uuid = 60ec8f00-e913-0018-1c69-febf3590ed2b
user.name = pc
osgi.framework.extensions = reference:file:org.eclipse.osgi.compatibility.state_1.0.200.v20160504-1419.jar,reference:file:org.eclipse.wst.jsdt.nashorn.extension_1.0.0.v201605131737.jar
org.osgi.framework.system.packages = javax.accessibility,javax.activation,javax.activity,javax.annotation,javax.annotation.processing,javax.crypto,javax.crypto.interfaces,javax.crypto.spec,javax.imageio,javax.imageio.event,javax.imageio.metadata,javax.imageio.plugins.bmp,javax.imageio.plugins.jpeg,javax.imageio.spi,javax.imageio.stream,javax.jws,javax.jws.soap,javax.lang.model,javax.lang.model.element,javax.lang.model.type,javax.lang.model.util,javax.management,javax.management.loading,javax.management.modelmbean,javax.management.monitor,javax.management.openmbean,javax.management.relation,javax.management.remote,javax.management.remote.rmi,javax.management.timer,javax.naming,javax.naming.directory,javax.naming.event,javax.naming.ldap,javax.naming.spi,javax.net,javax.net.ssl,javax.print,javax.print.attribute,javax.print.attribute.standard,javax.print.event,javax.rmi,javax.rmi.CORBA,javax.rmi.ssl,javax.script,javax.security.auth,javax.security.auth.callback,javax.security.auth.kerberos,javax.security.auth.login,javax.security.auth.spi,javax.security.auth.x500,javax.security.cert,javax.security.sasl,javax.sound.midi,javax.sound.midi.spi,javax.sound.sampled,javax.sound.sampled.spi,javax.sql,javax.sql.rowset,javax.sql.rowset.serial,javax.sql.rowset.spi,javax.swing,javax.swing.border,javax.swing.colorchooser,javax.swing.event,javax.swing.filechooser,javax.swing.plaf,javax.swing.plaf.basic,javax.swing.plaf.metal,javax.swing.plaf.multi,javax.swing.plaf.nimbus,javax.swing.plaf.synth,javax.swing.table,javax.swing.text,javax.swing.text.html,javax.swing.text.html.parser,javax.swing.text.rtf,javax.swing.tree,javax.swing.undo,javax.tools,javax.transaction,javax.transaction.xa,javax.xml,javax.xml.bind,javax.xml.bind.annotation,javax.xml.bind.annotation.adapters,javax.xml.bind.attachment,javax.xml.bind.helpers,javax.xml.bind.util,javax.xml.crypto,javax.xml.crypto.dom,javax.xml.crypto.dsig,javax.xml.crypto.dsig.dom,javax.xml.crypto.dsig.keyinfo,javax.xml.crypto.dsig.spec,javax.xml.datatype,javax.xml.namespace,javax.xml.parsers,javax.xml.soap,javax.xml.stream,javax.xml.stream.events,javax.xml.stream.util,javax.xml.transform,javax.xml.transform.dom,javax.xml.transform.sax,javax.xml.transform.stax,javax.xml.transform.stream,javax.xml.validation,javax.xml.ws,javax.xml.ws.handler,javax.xml.ws.handler.soap,javax.xml.ws.http,javax.xml.ws.soap,javax.xml.ws.spi,javax.xml.ws.spi.http,javax.xml.ws.wsaddressing,javax.xml.xpath,org.ietf.jgss,org.omg.CORBA,org.omg.CORBA_2_3,org.omg.CORBA_2_3.portable,org.omg.CORBA.DynAnyPackage,org.omg.CORBA.ORBPackage,org.omg.CORBA.portable,org.omg.CORBA.TypeCodePackage,org.omg.CosNaming,org.omg.CosNaming.NamingContextExtPackage,org.omg.CosNaming.NamingContextPackage,org.omg.Dynamic,org.omg.DynamicAny,org.omg.DynamicAny.DynAnyFactoryPackage,org.omg.DynamicAny.DynAnyPackage,org.omg.IOP,org.omg.IOP.CodecFactoryPackage,org.omg.IOP.CodecPackage,org.omg.Messaging,org.omg.PortableInterceptor,org.omg.PortableInterceptor.ORBInitInfoPackage,org.omg.PortableServer,org.omg.PortableServer.CurrentPackage,org.omg.PortableServer.POAManagerPackage,org.omg.PortableServer.POAPackage,org.omg.PortableServer.portable,org.omg.PortableServer.ServantLocatorPackage,org.omg.SendingContext,org.omg.stub.java.rmi,org.w3c.dom,org.w3c.dom.bootstrap,org.w3c.dom.css,org.w3c.dom.events,org.w3c.dom.html,org.w3c.dom.ls,org.w3c.dom.ranges,org.w3c.dom.stylesheets,org.w3c.dom.traversal,org.w3c.dom.views,org.w3c.dom.xpath,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers
guice.disable.misplaced.annotation.check = true
eclipse.launcher = D:\Myeclipse\eclipse-neno\eclipse.exe
eclipse.launcher.name = Eclipse
equinox.use.ds = true
osgi.frameworkClassPath = ., file:d:/Myeclipse/eclipse-neno/plugins/org.eclipse.osgi.compatibility.state_1.0.200.v20160504-1419.jar, file:d:/Myeclipse/eclipse-neno/plugins/org.eclipse.wst.jsdt.nashorn.extension_1.0.0.v201605131737.jar
org.osgi.framework.language = zh
org.eclipse.swt.internal.deviceZoom = 100
user.language = zh
org.osgi.framework.processor = x86-64
sun.boot.library.path = D:\jre\bin
osgi.syspath = d:\Myeclipse\eclipse-neno\plugins
osgi.compatibility.bootdelegation.default = true
org.osgi.framework.system.capabilities = osgi.ee; osgi.ee="OSGi/Minimum"; version:List<Version>="1.0, 1.1, 1.2",osgi.ee; osgi.ee="JRE"; version:List<Version>="1.0, 1.1",osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8",osgi.ee; osgi.ee="JavaSE/compact1"; version:List<Version>="1.8",osgi.ee; osgi.ee="JavaSE/compact2"; version:List<Version>="1.8",osgi.ee; osgi.ee="JavaSE/compact3"; version:List<Version>="1.8"
osgi.compatibility.bootdelegation = true
java.version = 1.8.0_91
user.timezone = Asia/Shanghai
org.osgi.framework.os.name = Windows10
osgi.bundles = reference:file:org.eclipse.osgi.compatibility.state_1.0.200.v20160504-1419.jar,reference:file:org.eclipse.wst.jsdt.nashorn.extension_1.0.0.v201605131737.jar,reference:file:org.eclipse.equinox.simpleconfigurator_1.1.200.v20160504-1450.jar@1:start
sun.arch.data.model = 64
java.endorsed.dirs = D:\jre\lib\endorsed
osgi.tracefile = D:\Myeclipse\workspace\.metadata\trace.log
file.encoding.pkg = sun.io
sun.jnu.encoding = GBK
sun.cpu.isalist = amd64
eclipse.application = org.eclipse.ui.ide.workbench
org.osgi.framework.vendor = Eclipse
equinox.init.uuid = true
file.separator = \
java.specification.name = Java Platform API Specification
java.class.version = 52.0
user.country = CN
org.eclipse.equinox.launcher.splash.location = D:\Myeclipse\eclipse-neno\\plugins\org.eclipse.platform_4.6.0.v20160606-1100\splash.bmp
java.home = D:\jre
osgi.os = win32
eclipse.commands = -os
win32
-ws
win32
-arch
x86_64
-showsplash
D:\Myeclipse\eclipse-neno\\plugins\org.eclipse.platform_4.6.0.v20160606-1100\splash.bmp
-launcher
D:\Myeclipse\eclipse-neno\eclipse.exe
-name
Eclipse
--launcher.library
D:\Myeclipse\eclipse-neno\\plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.400.v20160518-1444\eclipse_1617.dll
-startup
D:\Myeclipse\eclipse-neno\\plugins/org.eclipse.equinox.launcher_1.3.200.v20160318-1642.jar
--launcher.appendVmargs
-exitdata
428c_d0
-product
org.eclipse.epp.package.jee.product
-vm
C:\ProgramData\Oracle\Java\javapath\javaw.exe

java.vm.info = mixed mode
osgi.splashLocation = D:\Myeclipse\eclipse-neno\\plugins\org.eclipse.platform_4.6.0.v20160606-1100\splash.bmp
os.version = 10.0
osgi.arch = x86_64
path.separator = ;
java.vm.version = 25.91-b15
org.osgi.supports.framework.fragment = true
user.variant =
osgi.framework.shape = jar
java.awt.printerjob = sun.awt.windows.WPrinterJob
osgi.instance.area.default = file:/C:/Users/pc/workspace/
sun.io.unicode.encoding = UnicodeLittle
org.osgi.framework.version = 1.8.0
awt.toolkit = sun.awt.windows.WToolkit
user.script =
eclipse.stateSaveDelayInterval = 30000
osgi.install.area = file:/D:/Myeclipse/eclipse-neno/
osgi.framework = file:/d:/Myeclipse/eclipse-neno/plugins/org.eclipse.osgi_3.11.0.v20160603-1336.jar
user.home = C:\Users\pc
org.eclipse.equinox.simpleconfigurator.configUrl = file:org.eclipse.equinox.simpleconfigurator/bundles.info
osgi.framework.useSystemProperties = true
osgi.splashPath = platform:/base/plugins/org.eclipse.platform
java.specification.vendor = Oracle Corporation
osgi.nl = zh_CN
java.library.path = C:\ProgramData\Oracle\Java\javapath;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\ProgramData\Oracle\Java\javapath;C:\Program Files (x86)\Intel\iCLS Client\;C:\Program Files\Intel\iCLS Client\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files\Intel\Intel(R) Management Engine Components\IPT;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;D:\MySql\mysql-5.6;D:\MyAnt\ant-1.9.7\bin;D:\Mymaven\maven-3.3.9\bin;D:\MySql\mysql-5.6\bin;C:\Program Files (x86)\GtkSharp\2.12\bin;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;D:\My_Aspectj\bin;D:\My_android\android-sdk-windows\platform-tools;D:\My_android\android-sdk-windows\tools;C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\;C:\Program Files\Microsoft SQL Server\100\Tools\Binn\;C:\Program Files\Microsoft SQL Server\100\DTS\Binn\;C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\140\DTS\Binn\;C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\Microsoft SQL Server\130\DTS\Binn\;C:\Program Files\VisualSVN Server\bin;C:\Program Files\TortoiseSVN\bin;D:\jdk\bin;D:\jdk\jre\bin;D:\My_putty\;.
java.vendor.url = http://java.oracle.com/
eclipse.startTime = 1518875208947
org.osgi.framework.os.version = 10.0.0
eclipse.p2.data.area = @config.dir/../p2/
java.vm.vendor = Oracle Corporation
java.runtime.name = Java(TM) SE Runtime Environment
sun.java.command = D:\Myeclipse\eclipse-neno\\plugins/org.eclipse.equinox.launcher_1.3.200.v20160318-1642.jar -os win32 -ws win32 -arch x86_64 -showsplash D:\Myeclipse\eclipse-neno\\plugins\org.eclipse.platform_4.6.0.v20160606-1100\splash.bmp -launcher D:\Myeclipse\eclipse-neno\eclipse.exe -name Eclipse --launcher.library D:\Myeclipse\eclipse-neno\\plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.400.v20160518-1444\eclipse_1617.dll -startup D:\Myeclipse\eclipse-neno\\plugins/org.eclipse.equinox.launcher_1.3.200.v20160318-1642.jar --launcher.appendVmargs -exitdata 428c_d0 -product org.eclipse.epp.package.jee.product -vm C:\ProgramData\Oracle\Java\javapath\javaw.exe -vmargs -Dosgi.requiredJavaVersion=1.8 -XX:+UseG1GC -XX:+UseStringDeduplication -Dosgi.requiredJavaVersion=1.8 -Xms256m -Xmx1024m -jar D:\Myeclipse\eclipse-neno\\plugins/org.eclipse.equinox.launcher_1.3.200.v20160318-1642.jar
java.class.path = D:\Myeclipse\eclipse-neno\\plugins/org.eclipse.equinox.launcher_1.3.200.v20160318-1642.jar
osgi.requiredJavaVersion = 1.8
org.eclipse.update.reconcile = false
eclipse.vm = C:\ProgramData\Oracle\Java\javapath\javaw.exe
java.vm.specification.name = Java Virtual Machine Specification
java.vm.specification.version = 1.8
sun.cpu.endian = little
sun.os.patch.level =
gosh.args = --nointeractive
java.io.tmpdir = C:\Users\pc\AppData\Local\Temp\
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
applicationXMI = org.eclipse.ui.workbench/LegacyIDE.e4xmi
eclipse.product = org.eclipse.epp.package.jee.product
eclipse.home.location = file:/D:/Myeclipse/eclipse-neno/
os.arch = amd64
java.awt.graphicsenv = sun.awt.Win32GraphicsEnvironment
java.ext.dirs = D:\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
user.dir = D:\Myeclipse\eclipse-neno
org.osgi.supports.framework.requirebundle = true
line.separator =

java.vm.name = Java HotSpot(TM) 64-Bit Server VM
org.apache.commons.logging.Log = org.apache.commons.logging.impl.NoOpLog
file.encoding = GBK
eclipse.buildId = 4.6.0.I20160606-1100
eclipse.vmargs = -Dosgi.requiredJavaVersion=1.8
-XX:+UseG1GC
-XX:+UseStringDeduplication
-Dosgi.requiredJavaVersion=1.8
-Xms256m
-Xmx1024m
-jar
D:\Myeclipse\eclipse-neno\\plugins/org.eclipse.equinox.launcher_1.3.200.v20160318-1642.jar

java.specification.version = 1.8
org.osgi.framework.executionenvironment = OSGi/Minimum-1.0,OSGi/Minimum-1.1,OSGi/Minimum-1.2,JavaSE/compact1-1.8,JavaSE/compact2-1.8,JavaSE/compact3-1.8,JRE-1.1,J2SE-1.2,J2SE-1.3,J2SE-1.4,J2SE-1.5,JavaSE-1.6,JavaSE-1.7,JavaSE-1.8
osgi.logfile = D:\Myeclipse\workspace\.metadata\.log
osgi.configuration.area = file:/D:/Myeclipse/eclipse-neno/configuration/

4.jmap
jmap用于生成堆转储快照(一般称为heapdump或者dump文件)。jmap不仅能获取dump还可以查询finalize执行队列,java堆和永久代详细信息,空间使用率,当前用的是什么收集器等。

jmap语法格式如下:
jmap [option] pid
jmap [option] executable core
jmap [option] [server-id@]remote-hostname-or-ip


如果运行在64位JVM上,可能需要指定-J-d64命令选项参数。
jmap -J-d64 -heap pid

[ option ] 参数说明:
-dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件.
-finalizerinfo 打印正等候回收的对象的信息.
-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况.
-histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.
-permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来.
-F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效..

输出一个eclipse进程堆信息:
C:\Users\pc>jmap -heap 14416
Attaching to process ID 14416, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b15

using thread-local object allocation.
Garbage-First (G1) GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 1073741824 (1024.0MB)
   NewSize                  = 1363144 (1.2999954223632812MB)
   MaxNewSize               = 643825664 (614.0MB)
   OldSize                  = 5452592 (5.1999969482421875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 1048576 (1.0MB)

Heap Usage:
G1 Heap:
   regions  = 1024
   capacity = 1073741824 (1024.0MB)
   used     = 196334384 (187.2390594482422MB)
   free     = 877407440 (836.7609405517578MB)
   18.2850643992424% used
G1 Young Generation:
Eden Space:
   regions  = 58
   capacity = 101711872 (97.0MB)
   used     = 60817408 (58.0MB)
   free     = 40894464 (39.0MB)
   59.79381443298969% used
Survivor Space:
   regions  = 7
   capacity = 7340032 (7.0MB)
   used     = 7340032 (7.0MB)
   free     = 0 (0.0MB)
   100.0% used
G1 Old Generation:
   regions  = 124
   capacity = 159383552 (152.0MB)
   used     = 127128368 (121.23905944824219MB)
   free     = 32255184 (30.760940551757812MB)
   79.76253911068565% used

49493 interned Strings occupying 5410112 bytes.

查看一个eclipse进程等待回收对象信息:
C:\Users\pc>jmap -finalizerinfo 14416
Attaching to process ID 14416, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b15
Number of objects pending for finalization: 0

查看一个eclipse进程每个class的实例数目,内存占用,类全名信息:
 C:\Users\pc>jmap -histo 14416
 .......
 num     #instances         #bytes  class name  
 229:          1295          31080  org.eclipse.aether.repository.RepositoryPolicy
 230:           297          30888  org.eclipse.emf.ecore.impl.EAttributeImpl
 231:           964          30848  sun.security.util.DerInputBuffer
 232:           964          30848  sun.security.util.DerValue
 233:           549          30744  org.eclipse.e4.ui.model.application.commands.impl.CategoryImpl
 234:           768          30720  org.apache.lucene.index.FreqProxTermsWriter$PostingList
 235:           544          30464  com.sun.org.apache.xerces.internal.dom.ElementImpl
 236:          1898          30368  org.apache.maven.artifact.versioning.ComparableVersion$StringItem
 237:          1260          30240  org.eclipse.core.internal.dtree.DataTreeNode
 238:          1242          29800  [Lorg.eclipse.equinox.p2.metadata.ITouchpointData;
 239:           216          29768  [[[C
 240:           610          29280  org.eclipse.emf.common.util.URI$Hierarchical
 241:          1210          29256  [Ljava.lang.reflect.Type;
 242:          1219          29248  [Lorg.eclipse.equinox.p2.metadata.IArtifactKey;
 243:          1218          29232  org.eclipse.equinox.internal.p2.metadata.ArtifactKey
 244:           190          28880  org.apache.maven.plugin.descriptor.MojoDescriptor
 245:           190          28880  org.eclipse.emf.ecore.impl.EClassImpl
 246:           361          28880  org.eclipse.ui.internal.handlers.ActionDelegateHandlerProxy
 247:           257          28784  org.eclipse.jdt.internal.compiler.lookup.TypeSystem$HashedParameterizedTypes$InternalParameterizedTypeBinding
 248:           897          28704  org.eclipse.m2e.core.embedder.ArtifactKey
 249:          1186          28464  com.google.common.collect.SingletonImmutableList
 250:          1171          28104  com.google.common.collect.SingletonImmutableSet
 251:           585          28080  org.eclipse.jface.bindings.keys.KeyBinding
 252:           583          27984  java.util.zip.ZipFile
 ...

5.jhat
dump分析工具,上面讲过分析dump的工具还有MAT( Eclipse Memory Analyzer tool)、IBM HeapAnalyzer等,一般这个命令不太用到,是因为分析dump是个既耗时又耗机器资源的过程,第二个原因是这个工具比较简陋,没有MAT( Eclipse Memory Analyzer tool)、IBM HeapAnalyzer这些专业和强大。

命令格式:
jhat file

6.jstack
jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64"。
如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

windows命令格式 :
jstack [ option ] pid

参数说明:
-F当’jstack [-l] pid’没有相应的时候强制打印栈信息
-l长列表. 打印关于锁的附加信息,例如属于java.util.concurrent的ownable synchronizers列表.
-m打印java和native c/c++框架的所有栈信息.

linux命令格式:
jstack [option] pid
jstack [option] executable core
jstack [option] [server-id@]remote-hostname-or-ip


命令行参数选项说明如下:
-l long listings,会打印出额外的锁信息,在发生死锁时可以用jstack -l pid来观察锁持有情况
-m mixed mode,不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)

使用方式:
jstack可以定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多

7.hprof
hprof能够展现CPU使用率,统计堆内存使用情况。

语法格式如下:
java -agentlib:hprof[=options] ToBeProfiledClass
java -Xrunprof[:options] ToBeProfiledClass
javac -J-agentlib:hprof[=options] ToBeProfiledClass


[options]命令选项如下:
参数名称                              作用                                                 默认取值
---------------------                   -----------                                         -------
heap=dump|sites|all           堆分析                                            all
cpu=samples|times|old     CPU使用率                                   off
monitor=y|n                           监控竞争                                        n
format=a|b                             文本(TXT)或者二进制输出          a
file=<file>                               将数据保存到java.hprof[.txt]
net=<host>:<port>               使用socket发送数据                    off
depth=<size>                        栈跟踪深度                                     4
interval=<ms>                       采样时间间隔(ms)                      10
cutoff=<value>                       输出截止点                                    0.0001
lineno=y|n                              跟踪代码行号?                               y
thread=y|n                              线程跟踪?                                       n
doe=y|n                                  dump on exit?                                 y
msa=y|n                                 Solaris系统报告                             n
force=y|n                                强制输出到文件<file>                     y
verbose=y|n                          打印转储信息                                  y

8.调优策略



两个基本原则
将转移到老年代的对象数量降到最少。
减少Full GC的执行时间。目标是Minor GC时间在100ms以内,Full GC时间在1s以内。

主要调优参数

设定堆内存大小,这是最基本的。
-Xms:启动JVM时的堆内存空间。
-Xmx:堆内存最大限制。

设定新生代大小
新生代不宜太小,否则会有大量对象涌入老年代。
-XX:NewRatio:新生代和老年代的占比。
-XX:NewSize:新生代空间。
-XX:SurvivorRatio:伊甸园空间和幸存者空间的占比。
-XX:MaxTenuringThreshold:对象进入老年代的年龄阈值。

设定垃圾回收器
年轻代:-XX:+UseParNewGC。
老年代:-XX:+UseConcMarkSweepGC。
CMS可以将STW时间降到最低,但是不对内存进行压缩,有可能出现“并行模式失败”。比如老年代空间还有300MB空间,但是一些10MB的对象无法被顺序的存储。这时候会触发压缩处理,但是CMS GC模式下的压缩处理时间要比Parallel GC长很多。
G1采用”标记-整理“算法,解决了内存碎片问题,建立了可预测的停顿时间类型,能让使用者指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。


串行处理器:

-- 适用情况:数据量比较小(100M左右),单处理器下并且对相应时间无要求的应用。

-- 缺点:只能用于小型应用。

并行处理器:

-- 适用情况:“对吞吐量有高要求”,多CPU,对应用过响应时间无要求的中、大型应用。举例:后台处理、科学计算。

-- 缺点:垃圾收集过程中应用响应时间可能加长。

并发处理器:

-- 适用情况:“对响应时间有高要求”,多CPU,对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。



以下配置主要针对 分代垃圾回收算法而言。

堆大小设置

年轻代的设置很关键

JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32-bit 还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64位操作系统对内存无限制。在Windows Server 2003系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。

典型设置:

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k

-Xmx3550m:设置JVM最大可用内存为3550m。

-Xms3550m:设置JVM初始内存为3550m。此值可以设置与 -Xmx 相同,以避免每次垃圾回收完成后JVM重新分配内存。

-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小+年老代大小+持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256k。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5。

-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6。

-XX:MaxPermSize=16m:设置持久代大小为16m。

-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活时间,增加在年轻代被回收的概率。



回收器选择

JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动的时候加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。

吞吐量优先的并行收集器

如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学计算和后台处理等。

典型配置:

java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。

-XX:+ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。



java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。



java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100

-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。



java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开。



响应时间优先的并发收集器

如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。

典型配置:

java -Xmx3550m -Xms3550 -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

-XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。

-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。



java -Xmx3550m -Xms3550 -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。

-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片。



常见配置汇总

堆设置

-Xms:初始堆大小

-Xmx:最大堆大小

-XX:NewSize=n:设置年轻代大小

-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5。

-XX:MaxPermSize=n:设置持久代大小

收集器设置

-XX:+UseSerialGC:设置串行收集器

-XX:+UseParallelGC:设置并行收集器

-XX:+UseParalledlOldGC:设置并行年老代收集器

-XX:+UseConcMarkSweepGC:设置并发收集器

垃圾回收统计信息

-XX:+PrintGC

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-Xloggc:filename

并行收集器设置

-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。

-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间

-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+N)

并发收集器设置

-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。

-XX:+ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。


猜你喜欢

转载自blog.csdn.net/qq_34448345/article/details/79331386