关于JVM调优的那些事

前言:从不生产理论,只做理论的搬运工。本篇理论来源:https://tech.meituan.com/2017/12/29/jvm-optimize.html

GC优化不能解决一切性能问题,它是最后的调优手段。

基本思路

     CPU使用率较高,频繁地进行GC且GC时间长。那么我们就需要进行JVM调优,尽可能使每一次GC回收更多的对象。

 这里又可以分为两小块进行讨论:

①MinorGC,年轻代的内存使用率始终在高位,频繁的GC但是效率又不高,说明对象没有这么快被回收,活跃的数据量比较大,可以适当把年轻代调大一点。

②Full GC(Full= Minor+Major,通常来说是这么理解,看使用的是什么垃圾回收器),如果每次GC后,内存的使用率还是没有降下来,可以考虑是内存泄漏了;如果GC后内存使用率降下来了,说明年老代的空间不够,还需要调大一点。

具体的调优步骤

    调优之前,是不是得把JVM的常用参数在熟悉一下?如果你经常干这个,那就请直接跳过吧。

堆设置

  • -Xms:初始堆大小

  • -Xmx:最大堆大小

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

  • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4

  • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如: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数。并行收集线程数。

JVM中各分区的大小对GC的性能影响很大。那么要如何设计各分区的大小呢,分析活跃数据大小是很好的切入点。

这里呢,我们不用纠结该怎么分配比例了, 美团技术大佬给我们总结了,如下表:

空间 倍数
总大小 3-4 倍活跃数据的大小
新生代 1-1.5 活跃数据的大小
老年代 2-3 倍活跃数据的大小
永久代 1.2-1.5 倍Full GC后的永久代空间占用

例如:我们的活跃数据是300M,那么我们的Heap总大小就应该是300M*4=1200M;新生代:300M*1.5=450M;老年代:1200M-450M=750M。当然这个具体的大小可以根据自己应用程序的特性进行调整。

垃圾收集器的选择

     好了,确定了各个分区大小后,就是确定要使用的垃圾收集器了。JVM的内存模型决定了垃圾回收是分代进行的,其中,由于新生代进行Minor GC后,一般存活的对象较少,可以采用“复制”算法;而年老代存活的对象较多,一般采用“标记-整理”“标记-清除”算法。

    首先看到我们常用的垃圾回收器,如下表:

垃圾回收器 特性
串行(Serial)回收器 单线程的一个回收器,简单、易实现、效率高。
并行(ParNew)回收器 Serial的多线程版,可以充分的利用CPU资源,减少回收的时间。
吞吐量优先(Parallel Scavenge)回收器 侧重于吞吐量的控制
并发标记清除(CMS,Concurrent Mark Sweep)回收器 以获取最短回收停顿时间为目标的回收器,该回收器是基于“标记-清除”算法实现的。
Serial Old Serial Old是Serial的老年代版本。同Serial一样, 它也是单线程收集器。用的是“标记——整理”算法。
Parallel Old Parallel Old 是 Parallel Scavenge 的老年代版本,使用多线程和“标记——整理算法”。
G1(Garbage-First)收集器 G1 能充分利用多CPU的硬件优势,使用多个CPU来缩短“Stop The World”停顿时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。G1从整体来看是基于“标记-整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,这意味着G1运行期间不会产生内存空间碎片。

附上一张简图

关于JVM调优的那些事

 看完上面这些,你知道要怎么进行JVM的参数调优了吧。