# 技术杂谈

JVM性能优化之GC日志分析

2022-08-10 13:55:42
13

https://blog.csdn.net/qq_19586549/article/details/123285910 JVM性能优化之GC日志分析

https://blog.csdn.net/qq_19586549/article/details/123285910 前言

任何项目日志都是一个非常重要的部分,同样GC日志也是在垃圾回收中相当重要的,我们想要对JVM优化,首先要知道优化哪个地方,怎么找到需要优化的部位就需要读懂GC日志,通过GC日志来分析当前应用再某些情况下的缺点。

https://blog.csdn.net/qq_19586549/article/details/123285910 一、GC日志参数

要想分析GC日志,首先我们需要收集日志,收集什么呢?我们可以根据自己需要分析的内容去配置相应的参数,让GC日志按照固定的格式打印出相关的内容,这样日志数据就非常清晰了。

https://blog.csdn.net/qq_19586549/article/details/123285910 GC日志参数

这是一些常见的GC日志参数:

参数 说明
-XX:+PrintGC 打印简单GC日志
-XX:+PrintGCDetails 打印GC详细信息
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式)
-XX:+PrintGCApplicationStoppedTime 打印由GC日志产生的停顿时间
-XX:+UseGCLogFileRotation 滚动打印日志
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:文件路径 指定输出路径收集日志到日志文件

下面我们写段多线程创建对象的代码来看一下

public static void main(String[] args){
   for (int i = 0; i < 20; i++) {
       new Thread(()-> {
           List list = new ArrayList();
           for (int j = 0; j < 100000; j++) {
               list.add(new Object());
           }
       }).start();
   }
}

配置JVM参数(设置内存大小,打印详细日志、时间戳以及将GC日志输出到文件,这里需要注意日志路径文件夹必须手动创建好,否则报错找不到文件路径):

-Xms32m -Xmx32m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:F:/logs/gc.log

看一下gc.log中打印出来的日志

有点模糊,我们找出其中一条格式化后看一下具体的信息,最下面每个区的内存使用情况

2022-03-04T22:02:31.166+0800: 0.552: //这是时间戳信息
[Full GC  //这是触发的GC类型
(Ergonomics) //这是GC触发的原因
[PSYoungGen: 16080K->6148K(18944K)]  //这是新生代的信息,回收前的大小->回收后的大小(总大小)
[ParOldGen: 38844K->43780K(44032K)] //这是老年代的信息,回收前的大小->回收后的大小(总大小)
54924K->49929K(62976K),  //这是堆的信息,回收前的大小->回收后的大小(总大小)
[Metaspace: 4247K->4247K(1056768K)], //这是元空间的信息,回收前的大小->回收后的大小(总大小)
 0.1793855 secs] //这是本次GC耗时时间
[Times: user=0.72 sys=0.00, real=0.18 secs] //GC耗时具体  用户时间  系统时间 实际时间

https://blog.csdn.net/qq_19586549/article/details/123285910 常用的垃圾收集器配置

下面是一些常用的垃圾收集器配置:

参数 说明
UseSerialGC 使用Serial+Serial Old 收集器组合进行内存回收
UseParNewGC 使用 ParNew + Serial Old 收集器组合进行内存回收
UseConcMarkSweepGC 使用 ParNew + CMS + Serial Old 的收集器组合进行内存回收
UseParallelOldGC 使用 Parallel Scavenge + Parallel Old 的收集器组合
UseParallelGC 使用 Parallel Scavenge + Serial Old 的收集器组合
SurvivorRatio 新生代中 Eden 和任何一个 Survivor 区域的容量比值,默认为 8
PretenureSizeThreshold 直接晋升到老年代对象的大小,单位是Byte
UseAdaptiveSizePolicy 动态调整 Java 堆中各区域的大小以及进入老年代的年龄
ParallelGCThreads 设置并行 GC 时进行内存回收的线程数
GCTimeRatio GC 时间占总时间的比率,默认值为99,只在Parallel Scavenge 收集器有效
MaxGCPauseMillis 设置 GC 最大的停顿时间,只在 Parallel Scavenge 收集器有效
CMSInitiatingOccupancyFraction 设置 CMS 收集器在老年代空间被使用多少后触发垃圾收集,默认是68%,只在 CMS 收集器上有效
CMSFullGCsBeforeCompaction 设置 CMS 收集器在进行多少次垃圾回收之后启动一次内存碎片整理
UseG1GC 使用 G1 (Garbage First) 垃圾收集器
MaxGCPauseMillis 设置最大GC停顿时间(GC pause time)指标(target)
G1HeapRegionSize 指定G1收集器每个heap区的大小,默认值将根据 heap size 算出最优解. 最小值为1Mb, 最大值为32Mb
-XX:PretenureSizeThreshold 在老年代分配的对象大小,如果大于该值则进入老年代

具体的垃圾收集器可以看前面的博客 简单说明了几个常用的垃圾收集器。
使用如下:例如将GC设置为G1收集器,

-XX:+UseG1GC

我们再看一下执行后产生的日志,其他垃圾收集器产生的日志跟上面一样,G1垃圾收集器产生的格式不一样

具体的啥含义可以看这篇博客https://blog.csdn.net/zhanggang807/article/details/46011341

https://blog.csdn.net/qq_19586549/article/details/123285910 大对象回收

对于大对象提供了一个配置参数 -XX:PretenureSizeThreshold 如果大于该参数会直接进入到老年代来分配对象的空间,因为对象比较大新生代中来回的进行复制比较消耗性能。
设置参数:

-XX:PretenureSizeThreshold=5242880 //当对象占内存大于5M放到老年代

测试代码,创建3个10M的大对象

 public static void main(String[] args){
      byte[]  bigObject = new byte[10 * 1024*1024];
      byte[]  bigObject1 = new byte[10 * 1024*1024];
      byte[]  bigObject2 = new byte[10 * 1024*1024];
  }


可以看出来新生代的使用大小为不到3M,老年代的使用大小为30M+,所以三个10M的大对象都放到了老年代。

https://blog.csdn.net/qq_19586549/article/details/123285910 二、GC日志分析工具

同样的看GC日志是比较麻烦的,我们希望有一个可视化工具可以以图表的形式来展现GC日志信息,下面就介绍款GC日志分析工具。

https://blog.csdn.net/qq_19586549/article/details/123285910 GCeasy

GCeasy是一款比较好用的在线GC日志分析工具,以可视化图表的形式展现了GC日志的详细信息,其网址

https://gceasy.io/gc-index.jsp

在网页上我们选中上面产生的GC日志,然后点击分析会看到下面几个分析图表。

https://blog.csdn.net/qq_19586549/article/details/123285910 JVM memory size

这一部分是JVM内存大小,Generation是JVM区域,Allocated指的是每部分分配的大小,Peak指的是峰值时候的内存使用情况。分别有新生代、老年代、元空间和总内存大小的统计信息。右面是对左边表格数据的可视化。

https://blog.csdn.net/qq_19586549/article/details/123285910 Key Performance Indicators

这一部分是关键指标,Throughput指的是GC的吞吐量,因为我们写的是循环一直创建对象,所以一直在执行垃圾回收,吞吐量才2.377%;再下面是GC回收期间的平均停顿时间和最大停顿时间;再下面是GC执行的时间范围和数量统计以及占用总GC数量统计的百分比,我们可以看到执行时间为0~100ms的GC总共执行了191次,占比为94.09%。右面就是GC执行占比的柱状图显示

https://blog.csdn.net/qq_19586549/article/details/123285910 Interactive Graphs

这一部分是图表统计,主要有这几方面的统计:回收后堆内存大小折线图、回收前堆内存大小折线图、GC执行时间分布图散点图、GC回收掉的对象占用空间大小的散点图、新生代回收内存情况的折线图、老年代回收内存情况的折线图、元区间回收内存情况的折线图以及堆内存分配和晋升情况(新生代对象晋升为老年代对象)散点图。

https://blog.csdn.net/qq_19586549/article/details/123285910 GC Statistics

这部分是GC的统计情况,第一个图是Minor GC和Full GC回收对象的大小;第二幅图为GC执行的总时间;第三幅图为GC执行的平均时间;
后面的四个表格分别是GC统计信息、Minor GC统计信息、Full GC统计信息以及GC执行暂停统计信息。

https://blog.csdn.net/qq_19586549/article/details/123285910 Object Stats

这是对象的统计信息,分别为总共创建的对象大小,新生代晋升老年代的对象大小,平均每秒创建的对象大小和平局每秒晋升的对象大小。

https://blog.csdn.net/qq_19586549/article/details/123285910 Memory Leak

这里是统计的内存泄漏信息,我们写的代码中没有内存泄漏的情况所以就没有结果

https://blog.csdn.net/qq_19586549/article/details/123285910 GC Causes

这一部分是GC花费的时间信息,前面是GC执行的原因,后面为他们的统计信息。

除了这些之外还有其他的信息,我们的GC执行日志没有对应的结果就不一一展示了。

本文转自 https://blog.csdn.net/qq_19586549/article/details/123285910 ,如有侵权,请联系删除。

最后编辑于 2024-10-31 14:05:45