一、概述
Java所提倡的自动内存管理最终可以归结为自动的解决两个问题:给对象分配内存以及回收分配给对象的内存,对JVM参数进行合理的设置, 可以提升GC的效率,从而提升系统的性能。
二、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错误时你才需要去设置永久代内存。
避免新生代设置过小
- Yong GC 频率过高,Yong GC 的对象会过早进入老年代,若此时进入老年代的对象大于老年代剩余空间,将会触发 Full GC
- 可能导致Yong GC 的对象直接进入老年代,若此时进入老年代的对象大于老年代剩余空间,将会触发 Full GC
避免新生代设置过大
- Old Space 变小, Full GC频繁发生
- Yong GC 耗时大幅度增长
避免 Survivor区过小/过大
调大 SurvivorRatio 值, 即 Eden 空间变大,Survivor 空间变小, Yong GC 的频率会下降,但如有 Yong GC 后的对象没有被回收且大于 Survivor 空间,则会直接放入 Old Space, 引发 Full GC 的频率提高
调小 SurvivorRatio 值, 即 Eden 空间变小, Yong GC 频繁发生。 Survicor 空间变大, 可以存储更多存货的对象,减少直接进入Old Space 的几率
适当设置新生代对象的存活周期,可充分的回收对象,避免对象进入 Old Space
四、参考资料
《深入理解Java虚拟机》 – 周志明 第4章
6个JDK命令行工具的使用
如何优化 Java GC
JVM 内存区域大小参数设置
Jvm 系列(四):Jvm 调优-命令篇
Java 服务 GC 参数调优案例