写点什么

JVM 性能优化(三)G1 垃圾收集器,附大厂真题面经

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:2888 字

    阅读完需:约 9 分钟

当越来越多的对象晋升到老年代 Old Region 时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,既 Mixed GC,该算法并不是一个 Old GC,除了回收整个 Young Region,还会回收一部分的 Old Region,这里需要注意:是一部分老年代,而不是全部老年代,可以选择那些 Old region 进行收集,从而可以对垃圾回收的耗时时间进行控制,也要注意的是 Mixed GC 并不是 Full GC


Mixed GC 什么时候出发?由参数 -XX:InitiatingHeapOccupancyPercent=n决定。默认:45%,该参数的意思是:当老年代大小占用整个堆大小百分比达到该阈值时触发。


它的 GC 步骤分两步:


1. 全局并发标记(global concurrent marking)


2. 拷贝存活对象(evacuation)


3.2.1 全局并发标记


全局并发标记,执行过程分为五个步骤:


  • **初始标记(initial mark ,STW):**标记从根节点直接可达的对象,这个阶段会执行一次年轻代 GC,会产生全局停顿。

  • 根区域扫描(root region scan):


  1. G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象

  2. 该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收。


  • 并发标记(Concurrent Marking): G1 GC 在整个堆中查


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


找可访问的(存活的)对象,该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断


  • 重新标记(Remark,STW): 该阶段是 STW 回收,因为程序在运行,针对上一次的标记进行修改。

  • 清楚垃圾(Cleanup,STW): 轻点和重置标记状态,该阶段会 STW,这个阶段并不会实际上去做垃圾的收集,等待 evacuation 阶段来回收


3.2.2 拷贝存活对象


Evacuation 阶段是全暂停的,该阶段把一部分 Region 里的活对象拷贝到另一部分 Region 中,从而实现垃圾的回收清理。


四、G1 收集器相关参数




  • -XX:+UseG1GC: 使用 G1 垃圾收集器

  • -XX:MaxGCPauseMillis: 设置期望达到最大 GC 停顿时间指标(JVM 会尽力实现,但不保证达到),默认值是 200 毫秒

  • -XX:G1HeapRegionSize=n:


  1. 设置的 G1 区域的大小,值是 2 的幂,范围是 1MB 到 32MB 之间,目标是根据最小的 Java 堆大小划分出约 2048 个区域

  2. 默认 是堆内存的 1/2000


  • -XX:ParallelGCThreads=n: 设置 STW 工作线程数的值,将 n 的值设置为逻辑处理器的数量,n 的值与逻辑处理器的数量相同,最多为 8

  • -XX:ConcGCThreads=n: 设置并行标记的线程数,将 n 设置为并行垃圾回收线程数(ParallelGCThreads)的 1/4 左右

  • **-XX:InitiatingHeapOccupancyPercent=n: **设置出发标记周期的 java 堆占用率阈值,默认占用率是这个 Java 堆的 45%


五、测试



5.1 测试代码:

public class TestGC {


// 实现:不断的产生新的数据(对象),随机的去废弃对象(垃圾)


public static void main(String[] args) throws Exception {


List<Object> list = new ArrayList<>();


while (true){


int sleep = new Random().nextInt(100);


if(System.currentTimeMillis() % 2 ==0){


//当前的时间戳,是偶数


list.clear();


}else{


//向 List 中添加 1000 个对象


for (int i = 0; i < 10000; i++) {


Properties properties = new Properties();


properties.put("key_"+i,"value_"+System.currentTimeMillis()+i);


list.add(properties);


}


}


Thread.sleep(sleep);


}


}


}

5.2 测试参数:

-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+PrintGCDetails -Xmx256m


5.3 日志输出:

[GC pause (G1 Evacuation Pause) (young), 0.0027884 secs]


[Parallel Time: 2.2 ms, GC Workers: 8]


[GC Worker Start (ms): Min: 542.1, Avg: 542.1, Max: 542.2, Diff: 0.1]

扫描根节点

[Ext Root Scanning (ms): Min: 0.1, Avg: 0.3, Max: 0.5, Diff: 0.4, Sum: 2.1]

更新 RS 区域所消耗的时间

[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]


[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]


[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]


[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]

对象拷贝

[Object Copy (ms): Min: 1.6, Avg: 1.8, Max: 1.9, Diff: 0.4, Sum: 14.6]


[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.3]


[Termination Attempts: Min: 1, Avg: 5.5, Max: 7, Diff: 6, Sum: 44]


[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.2]


[GC Worker Total (ms): Min: 2.1, Avg: 2.1, Max: 2.2, Diff: 0.1, Sum: 17.2]


[GC Worker End (ms): Min: 544.2, Avg: 544.2, Max: 544.2, Diff: 0.0]


[Code Root Fixup: 0.0 ms]


[Code Root Purge: 0.0 ms]


[Clear CT: 0.1 ms] # 清空 CardTable


[Other: 0.5 ms]


[Choose CSet: 0.0 ms] # 选取 CSet


[Ref Proc: 0.4 ms] # 弱引用、软引用的处理耗时


[Ref Enq: 0.0 ms] # 弱引用、软引用的入队耗时


[Redirty Cards: 0.0 ms]


[Humongous Register: 0.0 ms] # 大对象区域注册耗时


[Humongous Reclaim: 0.0 ms] # 大对象区域回收耗时


[Free CSet: 0.0 ms]


[Eden: 12.0M(12.0M)->0.0B(13.0M) Survivors: 0.0B->2048.0K Heap: 12.0M(252.0M)->3282.5K(252.0M)] # 年轻代的大小统计


[Times: user=0.00 sys=0.00, real=0.00 secs]


五、对于 G1 垃圾回收器优化建议




  • 年轻代大小:


  1. 避免使用 -Xmn 选项或 -XX:NewRatio 等其他相关选项显示设置年轻代大小

  2. 固定年轻代的大小会覆盖暂停时间目标


  • 暂停时间目标不要太过严苛


  1. G1 GC 的吞吐量目标是 90% 的应用程序时间和 10%的垃圾回收时间

  2. 评估 G1 GC 的吞吐量时,暂停时间目标不要太严苛,目标太过严苛表示你愿意承受更多的垃圾回收开销,而这会直接影响到吞吐量


六、可视化 GC 日志分析工具



6.1 GC 日志输出参数

前面通过 -XX:PrintGCDetails可以对 GC 日志进行打印,我们就可以在控制台查看,这样虽然可以查看 GC 的信息,但是并不直观,可以借助于第三方的 GC 日志分析工具进行查看


在日志打印输出设计到的参数如下:


  • -XX:+PrintGC:输出 GC 日志

  • -XX:+PrintGCDetails:输出 GC 的详细日志

  • -XX:+PrintGCTimeStamps:输出 GC 的时间戳(以基准的时间的形式)

  • -XX:+PrintGCDateStamps:输出 GC 的时间戳(以日期的形式,如: 2020-05-04T21:25.234+0800)

  • -XX:+PrintHeapAtGC:在进行 GC 的前后打印出堆的信息

  • -Xloggc:…/logs/gc.log 日志文件的输出路径


测试参数:


-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xmx256m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:E://test//gc.log


运行 main 方法后,我们就可以在 E://test//gc.log下发现一个 gc.log 的日志文件:


6.2 GC Easy 可视化工具

GC Easy 是一款在线可视化工具,易用、功能强大,可以通过 GC 日志分析进行内存泄露检测、GC 暂停原因分析、JVM 配置建议优化等功能,而且是可以免费使用的


网站地址:http://gceasy.io



上传 gc.log 后,点击 Analyze,就可以查看日志报告了


JVM 的各个分代区域分配的内存及使用峰值的内存



关键性能指标: 吞吐量及 GC 暂停平均时间、最大时间、各个时间段的比例


  • Throughput 表示的是吞吐量

  • Latency 表示响应时间


  1. Avg Pause GC Time 平均 GC 时间

  2. Max Pause GC TIme 最大 GC 时间

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
JVM性能优化(三)G1垃圾收集器,附大厂真题面经