面试题:如何在FullGC中减少系统停顿时间,也就是如何进行JVM调优了。
面试题常规回答
根据业务系统的并发量和数据量调整一下堆内存大小,把内存调大就行。或者如果是大内存机器(超过16G)可以使用G1垃圾回收器,设置好垃圾回收的预期停顿时间就行。
然后另一块就是对系统代码进行优化,减少大对象、递归、及时设置为无效对象等。
面试题深入剖析
上面的回答不能说错,只是比较粗略,无法落地实践,也没有答到点上。接下来我们一步步讲解:
1.JVM内存预估
首先整理一下高并发的接口有哪些,QPS大概是多少,预估一下每秒产生的内存对象多大。因为一般来说,短时间内产生大量的垃圾对象就是由于高并发的接口造成的。
2.优化原则:尽量在Young GC回收对象,减少进入老年代的对象
这一步就根据上面的预估的对象容量,设计好新生代的容量以及Eden区与2个Survivor区的比例。
然后借助于JVM监控工具,类似jstat命令、Arthas工具,观察一下Young GC和FullGC的频率,老年代内存的增长速度,进一步优化新生代的设置。
另一点,通过老年代的内存增长速度和Young GC是否有关联关系,可以判断出是否产生了大对象,因此这里也要设置好大对象的阈值。
3.常规的JVM参数调优
常规的JVM参数设置包括:
一般JVM堆内存设置为机器内存的一半,比如8G的机器:-Xms4096M -Xmx4096M
。
新生代大小:-Xmn1024M
,那么此时老年代就是 -Xmx3072M
。新生代的Eden所占十分之几:-XX:SurvivorRatio=8
。
年龄阈值:-XX:MaxTenuringThreshold=5
(默认是15次后才会进入老年代)。
栈大小:-Xss1M
。
元空间大小(JDK1.8以后的永久代):-XX:PermSize=256M -XX:MaxPermSize=256M
。
大对象阈值:-XX:PretenureSizeThreshold=1M
。
默认的垃圾回收器(这个一般不用设置):-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
。
老年代达到多少百分比后才进行GC:-XX:CMSInitiatingOccupancyFaction=90
。
CMS默认情况下会在每次Full GC之后都执行一次内存碎片整理,这种参数不需要改变。
4.代码优化
代码问题在JVM调优中占比较小,一般是在出现系统GC频繁获取内存溢出的时候,需要根据监控情况进行优化。
举个例子,比如出现内存溢出的时候,使用jmap命令将内存使用情况dump下来,然后MAT工具查看占用内存最多的对象是谁,然后分析那个线程的调用栈,接着就可以看到是哪个方法引发的内存溢出,然后优化对应的代码即可。
G1垃圾回收器这块我们放在后续的文章讲解。