JVM常用调优方法

一、常用调优方法

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: eset.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****:指定大页的大小**

三、触发gc的条件和解决方案

1、System.gc()方法的调用

system.gc(), 此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。

解决方案:强烈建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过**-XX:+ DisableExplicitGC**来禁止RMI调用System.gc。

2、老年代代空间(old/Tenured)不足

老年代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:java.lang.OutOfMemoryError: Java heap space 为避免以上两种状况引起的Full GC

解决方案:调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组

3、永久区(perm)或者元数据区(matespace)空间不足(jdk<=7 ,在jdk8里面是metaspace)

JVM规范中运行时数据区域中的方法区,在HotSpot虚拟机中又被习惯称为永生代或者永生区,Permanet Generation中存放的为一些class的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下也会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:java.lang.OutOfMemoryError: PermGen space

解决方案:为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC

4、CMS GC时出现promotion failed和concurrent mode failure

对于采用CMS进行老年代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入老年代,而此时老年代也放不下造成的;concurrent mode failure是在执行CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足造成的(有时候“空间不足”是CMS GC时当前的浮动垃圾过多导致暂时性的空间不足触发Full GC)。

解决方案:增大survivor space、老年代空间或调低触发并发GC的比率(-XX:CMSInitiatingOccupancyFraction=70,预留空间为70%),但在JDK 5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况,可通过设置-XX: CMSMaxAbortablePrecleanTime=5(单位为ms)来避免。

5、统计得到的Minor GC晋升到旧生代(Eden到S2和S1到S2的和)的平均大小大于老年代的剩余空间

这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。例如程序第一次触发Minor GC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。当新生代采用PS GC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。除了以上4种状况外,对于使用RMI来进行RPC或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。

解决方案:通过在启动时通过**- java -Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC**来禁止RMI调用System.gc。

6、堆中分配很大的对象

所谓大对象,是指需要大量连续内存空间的java对象,例如很长的数组,此种对象会直接进入老年代,而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况就会触发JVM进行Full GC。

解决方案:为了解决这个问题,CMS垃圾收集器提供了一个可配置的参数,即**-XX:+UseCMSCompactAtFullCollection**开关参数,用于在“享受”完Full GC服务之后额外免费赠送一个碎片整理的过程,空间碎片问题没有了,但停顿时间不得不变长了,JVM设计者们还提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的

参考:

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BABFAFAE

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值