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

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

2年前
828805

在软件开发中,很明显,与大型应用程序相比,小而灵活的微服务可以提供更多的优势。而 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/

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

为你推荐

不起眼,但是足以让你有收获的JVM内存分析案例
分析 这个问题说白了,就是说有些int[]对象不知道是哪里来的,于是我拿他的例子跑了跑,好像还真有这么回事。点该 dump 文件详情,查看相关的 int[] 数组,点该对象的“被引用对象”,发现所
从一起GC血案谈到反射原理
前言 首先回答一下提问者的问题。这主要是由于存在大量反射而产生的临时类加载器和 ASM 临时生成的类,这些类会被保留在 Metaspace,一旦 Metaspace 即将满的时候,就会触发 Fu
关于内存溢出,咱再聊点有意思的?
概述 上篇文章讲了JVM在GC上的一个设计缺陷,揪出一个导致GC慢慢变长的JVM设计缺陷,可能有不少人还是没怎么看明白的,今天准备讲的大家应该都很容易看明白 本文其实很犹豫写不写,因为感觉没有
协助美团kafka团队定位到的一个JVM Crash问题
概述 有挺长一段时间没写技术文章了,正好这两天美团kafka团队有位小伙伴加了我微信,然后咨询了一个JVM crash的问题,大家对crash的问题都比较无奈,因为没有现场,信息量不多,碰到这类问题我
又发现一个导致JVM物理内存消耗大的Bug(已提交Patch)
概述 最近我们公司在帮一个客户查一个JVM的问题(JDK1.8.0_191-b12),发现一个系统老是被OS Kill掉,是内存泄露导致的。在查的过程中,阴差阳错地发现了JVM另外的一个Bug。这个B
JVM实战:优化我的IDEA GC
IDEA是个好东西,可以说是地球上最好的Java开发工具,但是偶尔也会卡顿,仔细想想IDEA也是Java开发的,会不会和GC有关,于是就有了接下来对IDEA的GC进行调优 IDEA默认JVM参数: -
不起眼,但是足以让你收获的JVM内存案例
今天的这个案例我觉得应该会让你涨姿势吧,不管你对JVM有多熟悉,看到这篇文章,应该还是会有点小惊讶的,不过我觉得这个案例我分享出来,是想表达不管多么奇怪的现象请一定要追究下去,会让你慢慢变得强大起来,
如何通过反射获得方法的真实参数名(以及扩展研究)
前段时间,在做一个小的工程时,遇到了需要通过反射获得方法真实参数名的场景,在这里我遇到了一些小小的问题,后来在部门老大的指导下,我解决了这个问题。通过解决这个问题,附带着我了解到了很多新的知识,我觉得