性能文章>全面对比5大GC的内存伸缩能力(译)>

全面对比5大GC的内存伸缩能力(译)原创

3年前
878206

在软件开发中,很明显,与大型应用程序相比,小而灵活的微服务可以提供更多的优势。而 JDK9 的 Jigsaw 更加有助于分解我们的 Java 应用程序,从而构建更适合云原生的应用程序和微服务。而随着服务的用户越来越多,我们的应用程序需要水平扩容。在这个扩容过程中,其在单个容器中的预配置资源消耗将复制到每个实例。如此一来,正确配置垃圾回收器,这个 Java 程序最基础的组件,将会很大程度的影响整个项目对资源的利用率。
微信图片_20191216144408.jpg
本文对比测试垃圾回收器的内存伸缩能力,总计覆盖 5 大 GC:

  • G1
  • Parallel
  • ConcMarkSweep (CMS)
  • Serial
  • Shenandoah

为了测试需要,准备了一段示例代码,github 地址为:https://github.com/jelastic/java-vertical-scaling-test 。代码非常简单,只有 3 个 java 类:

  1. MemoryUsage.java:每隔 3 秒利用 MemoryUsage 输出used/committed/max 内存。
  2. Load.java:不断分配内存,且每分配 100 次会 sleep 一段时间。
  3. Test.java:不断调用 Load.java 分配内存。

测试过程中配置的 JVM 参数如下:
java -XX:+Use[gc_name]GC -Xmx2g -Xms32m -jar app.jar [sleep]

说明:

  • gc_name:表示指定的具体垃圾回收器;
  • Xms:初始化内存大小;
  • Xmx:内存使用上限;
  • sleep:即控制 Load.java 的 sleep 时间;

G1GC

我们首先要测试的就是 G1,从 JDK9 以后,G1 已经取代 ParallelGC 成为默认的 GC。如果想在低版本 JDK 中使用 G1,那么需要通过参数 -XX:+UseG1GC 显示指定 GC。

G1 最大的优势就是能压缩空闲内存,而且不需要很长的停顿时间。并且能回收不再使用的内存。经过测试发现,G1 也是这方面表现最好的垃圾回收器。

现在,我们对 G1 进行 3 次测试,测试的 sleep 分别为 0,10,100。即表示内存分配速度由快到慢。

快速分配内存
对应的 JVM 参数如下,sleep 为 0,即持续不断的分配内存:

java -XX:+UseG1GC -Xmx2g -Xms32m -jar app.jar 0
对应的内存趋势图如下所示:
2.jpg
由图可知,发生 FGC 后,Reserved 和 Used 内存非常及时的下降到最低水平。所以:G1 的表现非常好,不再使用的内存归还给操作系统的速度非常快。

中速分配内存
对应的 JVM 参数如下,sleep 为 10:

java -XX:+UseG1GC -Xmx2g -Xms32m -jar app.jar 10
对应的内存趋势图如下所示:
3.jpg

  • 慢速分配内存

对应的 JVM 参数如下,sleep 为 100:

java -XX:+UseG1GC -Xmx2g -Xms32m -jar app.jar 100
对应的内存趋势图如下所示:
4.jpg
由图可知,Reserved 内存和 Used 内存几乎完全同步,因为可以得出结论:G1 并不会过多的申请内存。

  • AggressiveHeap

再做一个测试,配置 JVM 参数-XX:+AggressiveHeap,或者认为 Xmx 和 Xms 一样大,对应的 JVM 参数如下:

java -XX:+UseG1GC -Xmx2g -Xms2g
或者
java -XX:+UseG1GC -Xmx2g -XX:+AggressiveHeap
对应的内存趋势图如下所示:
5.jpg
由图可知,Reserved 内存一直和 Xmx 一样大,不会有任何改变,即使你的程序可能只需要几十兆甚至更少的内存,JVM 也不会把那些未使用的可用内存归还给操作系统。如此一来,其他程序就无法使用这些这些内存。

所以,如果想要你的应用程序能弹性使用内存,确保没有配置-XX:+AggressiveHeap,或者 Xms 小于 Xmx。

ParallelGC

ParallelGC 以高吞吐量为主要目标,我们配置如下 JVM 参数:

java -XX:+UseParallelGC -Xmx2g -Xms32m -jar app.jar 10
对应的内存趋势图如下所示:
6.jpg
由图可知,FGC 后未使用的内存没有被归还给操作系统(Reserved 内存曲线并没有下降)。配置了 ParallelGC 的 JVM 会一直持有这些内存,除非发生了重启行为。所以,ParallelGC 是不具备伸缩能力的垃圾回收器。

Serial&CMS

Serial 和 CMS 都是可伸缩的垃圾回收器。 但是效果与 G1 相比有一定的差距,它们需要 4 次 FGC 周期才能释放所有不再使用的内存。我们配置如下 JVM 参数:
java -XX:+UseSerialGC -Xmx2g -Xms32m -jar app.jar 10
对应的内存趋势图如下所示:7.jpg
配置 CMS 也是类似的效果,JVM 参数如下:
java -XX:+UseConcMarkSweepGC -Xmx2g -Xms32m -jar app.jar 10
对应的内存趋势图如下所示:
8.jpg
说明: 从 JDK9 开始,不再使用的内存可以通过 JVM 参数-XX:-ShrinkHeapInSteps,在第一次 FGC 后加速释放。

ShenandoahGC

ShenandoahGC 是 JDK12 推出的基于 Region 设计的全新垃圾回收器。它非常大的不同就是:ShenandoahGC 不需要 FGC 就能异步回收不再使用的内存并归还给操作系统。 Shenandoah 在探测到可用内存后,几乎能够立即清理垃圾然后把这部分内存归还给操作系统。让我们配置如下的 JVM 参数:

java -XX:+UseShenandoahGC -Xmx2g -Xms32m 
-XX:+UnlockExperimentalVMOptions
-XX:ShenandoahUncommitDelay=1000 
-XX:ShenandoahGuaranteedGCInterval=10000 -jar app.jar 10

对应的内存趋势图如下所示:
9.jpg
由图可知,ShenandoahGC 非常弹性(elastic),即使没有发生任何FGC,它也能马上把不再使用的内存归还给操作系统。

总结

根据上面的测试结果可知,只有 ParallelGC 不具备内存伸缩能力。而其他的 GC,例如:Serial,CMS,G1,ShenandoahGC 都具备内存伸缩能力。需要说明的是,具备伸缩能力的前提是 Xms 小于 Xmx,其伸缩能力上限由 Xmx 限制,伸缩能力下限由 Xms 限制。其中 Serial 和 CMS 的效果一般,G1 需要借助 FGC 才能将不再使用的内存归还给操作系统。至于 JDK12 带来的 ShenandoahGC,效果非常好,而且不需要依赖 FGC,异步就能完成完成内存伸缩。

Java 不断完善和适应不断变化的需求。因此,其对内存的贪婪对微服务和云托管程序来说不再是问题,因为已经有正确的工具和方法来正确地扩展它,清理垃圾并释放进程的资源。通过合理的配置,Java 对于所有项目都具有成本效益 - 从云原生到传统企业应用程序。

原文地址:https://jelastic.com/blog/tuning-garbage-collector-java-memory-usage-optimization/

点赞收藏
分类:标签:
阿飞Javaer
请先登录,感受更多精彩内容
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

记一次线上RPC超时故障排查及后续GC调优思路

记一次线上RPC超时故障排查及后续GC调优思路

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

【全网首发】从源码角度分析一次诡异的类被加载问题

【全网首发】从源码角度分析一次诡异的类被加载问题

6
0