0%

Java 虚拟机 7 :JVM监控与调优

一、概述

Java所提倡的自动内存管理最终可以归结为自动的解决两个问题:给对象分配内存以及回收分配给对象的内存,对JVM参数进行合理的设置, 可以提升GC的效率,从而提升系统的性能。

二、JVM监控与性能分析工具

JVM性能分析工具

jps

JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。

命令格式:

1
jps [option] [hostid]

jps option参数:

选项 作用
-q 只输出LVMID,省略主类的名称
-m 输出虚拟机进程启动时传递给主类main()函数的参数
-l 输出主类的全名,如果进程执行的是Jar包,输出Jar路径
-v 输出虚拟机进程启动时JVM参数

jstat

jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

命令格式:

1
jstat [option] LVMID [interval] [count]

参数:

  • [option] : 操作参数
  • LVMID : 本地虚拟机进程ID
  • [interval] : 连续输出的时间间隔
  • [count] : 连续输出的次数

Jstat option参数:

选项 作用
-class 监视类装载、卸载数量、总空间以及类装载所消耗的时间
-gc 监视Java堆状况,包括Eden区、两个survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息
-gccapacity 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间
-gcutil 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比
-gccause 与-gcutil功能一样,但是会额外输出导致上一次GC产生的原因
-gcnew 监视新生代GC状况
-gcnewcapacity 监视内容与-gcnew相同,输出主要关注使用到的最大、最小空间
-gcold 监视老年代GC状况
-gcoldcapacity 监视内容与-gcold相同,输出主要关注使用到的最大、最小空间
-gcpermcapacity 输出永久代使用到的最大、最小空间
-compiler 输出JIT编译器编译过的方法、耗时等信息
-printcompilation 输出已经被JIT编译的方法

jinfo

jinfo(JVM Configuration info)这个命令作用是实时查看和调整虚拟机运行参数。 之前的jps -v口令只能查看到显示指定的参数,如果想要查看未被显示指定的参数的值就要使用jinfo口令

命令格式:

1
jinfo [option] [args] LVMID

jinfo option 参数:

选项 作用
-flag 输出指定args参数的值
-flags 不需要args参数,输出所有JVM参数的值
-sysprops 输出系统属性,等同于System.getProperties()

jmap

jmap(JVM Memory Map)命令用于生成heap dump文件,如果不使用这个命令,还阔以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候·自动生成dump文件。 jmap不仅能生成dump文件,还阔以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。

命令格式:

1
jmap [option] LVMID

jmap option参数:

选项 作用
-dump 生成Java堆转储快照。格式为:-dump:[live, ]format=b,file=filename,其中live子参数说明是否只dump出存活的对象
-finalizerinfo 显示在F-Queue中等待Finalizer县城执行finalize方法的对象。只在Linux/Solaris平台下有效
-heap 显示Java堆详细信息,如使用哪种回收器、参数配置、分代状况等。只在Linux/Solaris平台下有效
-histo 显示堆中对象统计信息,包括类、实例数量、合计容量
-permstat 以ClassLoader为统计口径显示永久代内存状态。只在Linux/Solaris平台下有效
-F 当虚拟机进程对-dump选项没有响应时,可使用这个选项强制生成dump快照。只在Linux/Solaris平台下有效

jhat

jhat(JVM Heap Analysis Tool)命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。

命令格式:

1
jhat [dumpfile]

jstack

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息

命令格式:

1
jstack [option] LVMID

jstack option 参数:

选项 作用
-F 当正常输出的请求不被响应时,强制输出线程堆栈
-l 除堆栈外,显示关于锁的附加信息
-m 如果调用到本地方法的话,可以显示C/C++的堆栈

三、JVM 参数调优

Java所提倡的自动内存管理最终可以归结为自动的解决两个问题:给对象分配内存以及回收分配给对象的内存,对JVM参数进行合理的设置, 可以提升GC的效率,从而提升系统的性能。

GC优化的两个目的:

将进入老年代的对象数量降到最低

除了可以在JDK 7及更高版本中使用的G1收集器以外,其他分代GC都是由Oracle JVM提供的。关于分代GC,就是对象在Eden区被创建,随后被转移到Survivor区,在此之后剩余的对象会被转入老年代。也有一些对象由于占用内存过大,在Eden区被创建后会直接被传入老年代。老年代GC相对来说会比新生代GC更耗时,因此,减少进入老年代的对象数量可以显著降低Full GC的频率。你可能会以为减少进入老年代的对象数量意味着把它们留在新生代,事实正好相反,新生代内存的大小是可以调节的。

减少Full GC的执行时间

Full GC的执行时间比Young GC要长很多,因此,如果在Full GC上花费过多的时间(超过1s),将可能出现超时错误。如果通过减小老年代内存来减少Full GC时间,可能会引起OutOfMemoryError或者导致Full GC的频率升高。另外,如果通过增加老年代内存来降低Full GC的频率,Full GC的时间可能因此增加。因此,你需要把老年代的大小设置成一个“合适”的值。

GC调优的主要参数

JVM内存各个代的大小,会直接影响 Yong GC 和 Full GC 触发的时机和触发的频率。在代大小的调优上,最关键的参数为:

类型 参数 描述
堆内存大小 -Xms -Xmx -Xms是启动JVM时堆内存的大小,-Xmn是堆内存的最大限制。通常设置为相同的值,避免运行时要不断的扩展JVM空间,造成性能上的损失
新生代空间大小 -Xmn 新生代大小 如:-Xmn256m 设置JVM的新生代内存大小(-Xmn 是将NewSize与MaxNewSize设为一致。 256m)
-XX:NewRatio 新生代和老年代的内存比,如:NewRatio等于2,则新生代:老年代=1:2,因此,NewRatio的值设置得越大,则老年代空间越大,新生代空间越小。
-XX:SurvivorRatio 新生代中 Eden区和Survivor区的内存比例,默认为 8
年龄设置 -XX:MaxTenuringThreshold 对象经历多少次 Yong GC 后 进入 Old Space. 默认 15

在进行GC优化时最常用的参数是-Xms,-Xmx和-XX:NewRatio。-Xms和-Xmx参数通常是必须的,所以NewRatio的值将对GC性能产生重要的影响。有些人可能会问如何设置永久代内存大小,你可以用-XX:PermSize和-XX:MaxPermSize参数来设置方法区的大小,但是要记住,只有当出现OutOfMemoryError错误时你才需要去设置永久代内存。

  1. 避免新生代设置过小

    • Yong GC 频率过高,Yong GC 的对象会过早进入老年代,若此时进入老年代的对象大于老年代剩余空间,将会触发 Full GC
    • 可能导致Yong GC 的对象直接进入老年代,若此时进入老年代的对象大于老年代剩余空间,将会触发 Full GC
  2. 避免新生代设置过大

    • Old Space 变小, Full GC频繁发生
    • Yong GC 耗时大幅度增长
  3. 避免 Survivor区过小/过大

    • 调大 SurvivorRatio 值, 即 Eden 空间变大,Survivor 空间变小, Yong GC 的频率会下降,但如有 Yong GC 后的对象没有被回收且大于 Survivor 空间,则会直接放入 Old Space, 引发 Full GC 的频率提高

    • 调小 SurvivorRatio 值, 即 Eden 空间变小, Yong GC 频繁发生。 Survicor 空间变大, 可以存储更多存货的对象,减少直接进入Old Space 的几率

  4. 适当设置新生代对象的存活周期,可充分的回收对象,避免对象进入 Old Space

四、参考资料

《深入理解Java虚拟机》 – 周志明 第4章
6个JDK命令行工具的使用
如何优化 Java GC
JVM 内存区域大小参数设置
Jvm 系列(四):Jvm 调优-命令篇
Java 服务 GC 参数调优案例