77

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

 4 years ago
source link: https://www.tuicool.com/articles/jEVBJna
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

Mbeu6n3.jpg!web

本文对比测试垃圾回收器的内存伸缩能力,总计覆盖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

对应的内存趋势图如下所示:

IVbyqqF.jpg!web g1 memory

由图可知,发生FGC后,Reserved和Used内存非常及时的下降到最低水平。所以: G1的表现非常好,不再使用的内存归还给操作系统的速度非常快

  • 中速分配内存

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

java -XX:+UseG1GC -Xmx2g -Xms32m -jar app.jar 10

对应的内存趋势图如下所示:

bmMfemn.jpg!web g1 memory
  • 慢速分配内存

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

java -XX:+UseG1GC -Xmx2g -Xms32m -jar app.jar 100

对应的内存趋势图如下所示:

bIV73q7.jpg!web g1 memory

由图可知,Reserved内存和Used内存几乎完全同步,因为可以得出结论:G1并不会过多的申请内存。

  • AggressiveHeap

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

java -XX:+UseG1GC -Xmx2g -Xms2g
或者
java -XX:+UseG1GC -Xmx2g -XX:+AggressiveHeap

对应的内存趋势图如下所示:

BnEJb2e.jpg!web g1 memory

由图可知,Reserved内存一直和Xmx一样大,不会有任何改变,即使你的程序可能只需要几十M甚至更少的内存,JVM也不会把那些未使用的可用内存归还给操作系统。如此一来,其他程序就无法使用这些这些内存。

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

ParallelGC

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

java -XX:+UseParallelGC -Xmx2g -Xms32m -jar app.jar 10

对应的内存趋势图如下所示:

vyeeaia.jpg!web parallel memory

由图可知,FGC后未使用的内存没有被归还给操作系统(Reserved内存曲线并没有下降)。配置了ParallelGC的JVM会一直持有这些内存,除非发生了重启行为。所以, ParallelGC是不具备伸缩能力的垃圾回收器

Serial&CMS

Serial和CMS都是可伸缩的垃圾回收器。但是效果与G1相比有一定的差距,它们需要4次FGC周期才能释放所有不再使用的内存。我们配置如下JVM参数:

java -XX:+UseSerialGC -Xmx2g -Xms32m -jar app.jar 10

对应的内存趋势图如下所示:

RVBR7j7.jpg!web serial memory

配置CMS也是类似的效果,JVM参数如下:

java -XX:+UseConcMarkSweepGC -Xmx2g -Xms32m -jar app.jar 10

对应的内存趋势图如下所示:

7Zr22y6.jpg!web cms memory

说明:从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

对应的内存趋势图如下所示:

BNzUner.jpg!web shenandoah memory

由图可知,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/

相关阅读

↓↓↓

Shenandoah GC:一个来自OpenJDK12的全新并发压缩垃圾回收器

译:谁是JDK8中最快的GC

从CMS到G1:LinkedIn个人主页调优实战

深(浅)入(出)剖析G1(Garbage First)

【阿飞的博客】 公众号二维码

↓↓↓↓

UZ7B7n7.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK