对于java应用我们可以通过一些配置把程序运行过程中的gc日志全部打印出来,然后分析gc日志得到关键性指标,分析 GC原因,调优JVM参数。
打印GC日志方法,在JVM参数里增加参数
‐XX:+PrintGCDetails
‐XX:+PrintGCTimeStamps
‐XX:+PrintGCDateStamps
‐Xloggc:./gc.log
Tomcat则直接加在JAVA_OPTS变量里。
启动一个java程序,加入以上参数
java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:./gc.log -jar application.jar
CommandLine flags: -XX:InitialHeapSize=198572224 -XX:MaxHeapSize=3177155584 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
我们可以看到图中第一行红框,是项目的配置参数。这里不仅配置了打印GC日志,还有相关的VM内存参数。
2019-09-30T14:57:12.993+0800: 4.615: [Full GC (Metadata GC Threshold) [PSYoungGen: 3104K->0K(200704K)] [ParOldGen: 3407K->6253K(80896K)] 6511K->6253K(281600K), [Metaspace: 20585K->20585K(1069056K)], 0.0798113 secs] [Times: user=0.16 sys=0.00, real=0.08 secs]
第二行红框中的是在这个GC时间点发生GC之后相关GC情况。
通过GC日志可以发现,因为Metadata GC Threshold的原因,即元空间不足导致FullGC,所以可以通过添加元空间参数来优化JVM
java -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:./gc-1.log -jar application.jar
优化后的GC日志
从上图可看出,扩大了元空间后并没有Full GC出现,达到了优化目的。
上面的这些参数,能够帮我们查看分析GC的垃圾收集情况。但是如果GC日志很多很多,成千上万行。就算你一目十行, 看完了,脑子也是一片空白。所以我们可以借助一些功能来帮助我们分析,这里推荐一个gceasy(https://gceasy.io),可以 上传gc文件,然后他会利用可视化的界面来展现GC情况。具体下图所示
上图我们可以看到年轻代,老年代,以及永久代的内存分配,和最大使用情况。
上图我们可以看到堆内存在GC之前和之后的变化,以及其他信息。 这个工具还提供基于机器学习的JVM智能优化建议,当然现在这个功能需要付费
使用ParNewGC作为年轻代垃圾收集器,ConcMarkSweepGC作为老年代垃圾收集器。
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
java -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:./gc-cms.log -jar application.jar
看下此情况的GC日志是如何的
执行CMS的GC,以下是CMS过程分析图,后续会对CMS的GC进行分析。
添加参数
-Xms100M -Xmx100M -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:D:/gclog/gc-g1.log
示例代码:
import java.util.ArrayList;
import java.util.List;
public class HeapTest {
byte [] b = new byte[1024*100];//100KB
public static void main(String[] args) throws InterruptedException {
List<HeapTest> list = new ArrayList<HeapTest>();
while (true){
list.add(new HeapTest());
}
}
}
JAVA资深架构师成长路线->架构师筑基必备技能->JVM性能深度调优