一、常用调优方法
1、将新对象预留在新生代
由于 Full GC 的成本要远远高于 Minor GC ,因此尽可能将对象分配在新生代,在JVM 调优中,可以为应用程序分配一个合理的新生代空间,以最大限度避免新对象直接进去老年代。
注意:由于新生代垃圾回收的速度高于老年代回收,因此,将年轻对象预留在新生代有利于提高整体的 GC 效率
2、大对象进入老年代
大对象占用空间多,直接放入新生代中会扰乱新生代GC,新生代空间不足将会把大量的较小的年轻代对象移入到老年代中,这对GC来说是相当不利的。如果有短命大对象,对GC来说将会是一场灾难,原本存放于老年代的永久对象,被短命大对象塞满,扰乱了分代内存回收的基本思路,因此,在开发过程中,尽可能避免使用短命的大对象。使用参数 -XX:PretenureSizeThreshold 设置大对象直接进入老年代的阀值,当对象超过这个阀值时,将直接在老年代中分配。其中, -XX:PretenureSizeThreshold 只对串行收集器和新生代并行收集器有效,并行回收收集器不识别这个参数。
注意:短命的大对象对垃圾回收是一场灾难,目前木有一种特别好的回收方法处理这个问题,因此尽可能避免使用短命的大对象。
3、设置对象进入老年代的年龄
在堆中每个对象都有自己的年龄,如果对象在 eden 区,经过一次 GC 后还存活,则被移动到 survivor 区中,对象年龄加 1,以后每经过一次 GC 依然存活的,对象年龄就加 1。当对象年龄达到阀值时,就移动到老年代,这个阀值用以下参数设置:
-XX:MaxTenuringThreshold:默认值是15,这个参数是指定进入老年代的最大年龄值,对象实际进入老年代的年龄是 JVM 在运行时根据内存使用情况动态计算的。
如果希望对象尽可能长地留在新生代中,可以设置一个较大的阀值。
4、稳定与震荡的堆大小
稳定的堆大小对垃圾回收是有利的,获得一个稳定堆大小的方法就是设置 -Xmx 和 -Xms 一样的值。不稳定的堆也不是木有用处,让堆大小在一个区间内震荡,在系统不需要使用大内存时压缩堆空间,使 GC 应对一个较小的堆,可以加快单次 GC 的速度。基于这种思想,JVM 提供了两个参数用于压缩和扩展堆空间,参数如下:
-XX:MinHeapFreeRatio:设置堆空间最小空闲比例,默认是 40 ,当堆空间的空闲比例小于这个值时,JVM 便会扩展堆空间
-XX:MaxHeapFreeRatio:设置堆空间的最大空闲比例,默认是 70,当堆空间的空闲比例大于这个值时,JVM 便会压缩堆空间,得到一个较小的堆
注意:当 -Xms 和 -Xmx 相等时,-XX:MinHeapFreeRatio 和 -XX:MaxHeapFreeRatio 这两个参数无效
5、吞吐量优先设置
机器配置是 4G 内存 和 32 核 CPU,配置参数如下:
-Xms3800m -Xmx3800m(堆的初始值和最大值一样)
-Xmn2g(新生代大小)
-Xss128k(线程栈大小,减少它使剩余的系统内存支持更多的线程)
-XX:+UseParallelGC(新生代使用并行回收收集器)
-XX:ParallelGCThreads=20(垃圾回收的线程数)
-XX:+UseParallelOldGC (老年代使用并行回收收集器)
6、使用大页案例
使用大的内存分页可以增强 CPU 的内存寻址能力,从而提高系统的性能,参数设置如下:
-XX:LargePageSizeInBytes:设置大页的大小
7、降低停顿案例
为了降低应用软件在垃圾回收时的停顿,首先考虑的使用关注系统停顿的 CMS 回收器,为了减少 Full GC 的次数,应尽可能将对象预留在新生代,新生代 Minor GC 的成本远远小于老年代的 Full GC
-Xms3800m -Xmx3800m(堆的初始值和最大值一样)
-Xmn2g(新生代大小)
-Xss128k(线程栈大小,减少它使剩余的系统内存支持更多的线程)
-XX:ParallelGCThreads=20(垃圾回收的线程数)
-XX:+UseConcMarkSweepGC(老年代使用 CMS 收集器)
-XX:+UseParNewGC(新生代使用并行收集器)
-XX:SurvivorRatio=8(设置 eden : survivor = 8 : 1)
-XX:TargetSurvivorRatio(设置 survivor 的使用率为 90%,默认是50%,提高了survivor 区的使用率,当存放的对象超过这个数值,则对象会向老年代压缩)
-XX:MaxTenuringThreshold=31(设置年轻对象晋升到老年代的最大年龄是31,默认是15,设为31是尽可能地将对象留在新生代)
二、常用JVM参数
1、JIT编译参数
JVM 的 JIT(Just-In-Time)编译器,可以在运行时将字节码编译成本地代码,从而提高函数的执行效率。参数设置如下
-XX:CompileThreshold:JIT 编译的阀值,当函数的调用超过这个值时,JIT 就将字节码编译成本地机器码。在 client 模式下,取值是 1500,在 server 模式下,取值是 10000
2、堆快照(堆 Dump)
在性能问题排查中,分析堆快照(Dump)是必不可少的一环。获取程序的堆快照文件有多种方法,下面介绍一种常用的方法,即使用 -XX:+HeapDumpOnOutOfMemoryError 参数在程序发生 OOM 时,导出应用程序的当前堆快照。这是一种非常有效的方法,因为当程序发生 OOM 退出系统时,一些瞬时信息都随着程序的终止而消失,而重现 OOM 问题往往比较困难或者耗时,因此当发生 OOM 时,将堆信息保存到文件中是至关重要的,通过下面的参数设置:
-XX:+HeapDumpOnOutOfMemoryError(开启堆快照)
-XX:HeapDumpPath=C:/m.hprof(保存文件到哪个目录)
导出的 Dump 文件可以通过 Visual VM 等多种工具查看分析,进而定位问题,如下图所示:
3、错误处理
系统发生 OOM 错误时,JVM 在错误发生时运行一段第三方脚本,重置系统,设置参数如下:
-XX:OnOutOfMemoryError=C:\reset.bat
4、获取 GC 信息
获取 GC 信息是 java 应用程序调优最重要的一环,下面介绍一些常用的设置参数:
-XX:+PrintGC(-verbose:gc):输出打印简要的 GC 信息,包括 GC 前的堆栈情况和 GC 后的堆栈大小和堆栈的总大小
-XX:+PrintGCDetails:输出打印详细的 GC 信息,不仅包括基本信息,还给出了新生代、老年代和永久区各自的 GC 信息
-XX:+PrintGCTimeStamps:额外输出 GC 发生的时间,可以推断出 GC 的频率和间隔
-XX:+PrintTenuringDistribution -XX:MaxTenuringThreshold=18:查看新生代晋升老年代的实际阀值(-XX:+PrintTenuringDistribution),设置的最大年龄为18( -XX:MaxTenuringThreshold=18)
-XX:+PrintHeapAtGC:每次 GC 时都会打印堆的详细使用情况,输出量很巨大。它分为两个部分:GC 前的堆信息和 GC 后的堆信息,这里包含了新生代、老年代和永久区的使用大小和使用率,还包括了新生代中 eden 区和 survivor 区的使用情况
-XX:+PrintGCApplicationStoppedTime:应用程序在 GC 发生时的停顿时间
-XX:+PrintGCApplicationConcurrentTime:应用程序在 GC 停顿期间的执行时间
-Xloggc:C:/gc.log:将 GC 日志信息输出到具体位置的文件中,便于日后日志分析
注意:详细的 GC 信息是进行 JVM 调优的重要参考信息,可以根据 GC 日志,设置合理的堆大小及相关垃圾回收器的参数
5、类和对象跟踪
JVM 提供了一组参数,用于获取系统运行时加载、卸载类的信息,参数设置如下:
-XX:TraceClassLoading:跟踪类加载情况
-XX:TraceClassUnloading:跟踪类的卸载情况
-verbose:class:相当于同时设置 -XX:TraceClassLoading 和 -XX:TraceClassUnloading 两个参数
-XX:+PrintClassHistogram:打印运行时实例的信息,当设置此参数后,使用 Ctrl + Break 会输出系统内类的统计信息,从左到右依次显示了序号、实例数量、总大小和类名等信息
6、控制GC
-XX:DisableExplicitGC:禁止 GC 操作,即禁止在程序中使用 System.gc() 触发 Full GC
-Xnoclassgc:禁止类的回收
-Xingc:增量式的 GC ,增量式的 GC 使用特定的算法让 GC 线程和应用程序线程交叉执行,从而减少应用程序因 GC 产生的停顿时间
-Xverify:none:关闭类校验器
-XX:+UseLargePages:启用大页,使用大页后,内存分页的表项就会减少,从而提升CPU从虚拟内存地址映射到物理内存地址的能力
-XX:LargePageSizeInBytes:指定大页的大小