就软件而言,大小很重要。很明显,与大单体方法相比,在微服务架构中使用小块可以带来更多优势。 Jigsaw 的最新 Java 版本有助于分解遗留应用程序或从头构建新的云原生应用程序。
这种方法减少了磁盘空间、构建时间和启动时间。但是,它对 RAM 使用管理没有足够的帮助。众所周知,Java 在很多情况下都会消耗大量的内存。与此同时,许多人没有注意到 Java 在内存使用方面变得更加灵活,并提供了满足微服务要求的功能。在本文中,我们将分享我们的经验,如何调整 Java 进程中的 RAM 使用以使其更具弹性并获得更快扩展和更低总拥有成本 (TCO) 的好处。
有三种缩放选项:垂直、水平和两者的组合。为了最大化结果,首先您需要以最佳方式设置垂直缩放。然后,当你的项目横向增长时,单个容器内预配置的资源消耗将被复制到每个实例中,效率将成比例增长。
如果配置得当,垂直扩展对微服务和整体应用都非常有效,可以根据容器内的当前负载优化内存和 CPU 使用率。选择的垃圾收集器是主要的基础砖之一,其设置可以影响整个项目。
OpenJDK 有五种广泛使用的垃圾收集器解决方案:
让我们看看它们在缩放方面的表现如何,以及可以应用哪些设置来改善结果。
为了进行测试,我们将使用有助于跟踪 JVM 垂直缩放结果的示例 Java 应用程序: https://github.com/jelastic/java-vertical-scaling-test
每次 GC 测试都会启动以下 JVM 启动选项:
java -XX:+Use[gc_name]GC -Xmx2g -Xms32m -jar app.jar [sleep]
在哪里:
目前,需要调用 Full GC 才能正确释放未使用的资源。它可以通过各种选项轻松启动:
jcmd <pid> GC.run
– 执行外部调用System.gc()
– 在源代码里面jvisualvm
– 通过出色的 VisualVM 故障排除工具手动操作-javaagent:agent.jar
– 可插入的常用方法。 Github 存储库 Java Memory Agent 提供了开源自动化插件。可以在输出日志中跟踪内存使用情况,或使用 VisualVM 进行更深入的审查。
对于 Java 生态系统来说,好消息是从 JDK 9 开始,默认启用现代收缩 G1 垃圾收集器。如果您使用较低版本的 JDK,可以使用 -XX:+UseG1GC
参数启用 G1。
G1 的主要优势之一是能够压缩可用内存空间,而无需长时间暂停和取消提交未使用的堆。我们发现此 GC 是垂直扩展在 OpenJDK 或 HotSpot JDK 上运行的 Java 应用程序的最佳选择。
为了更好地了解 JVM 在不同内存压力水平下的行为,我们将运行三种情况:1) 快速,2) 中等,以及 3) 缓慢的内存使用增长。通过这种方式,我们可以检查 G1 ergonomic 的智能程度以及 GC 如何处理不同的内存使用动态。
java -XX:+UseG1GC -Xmx2g -Xms32m -jar app.jar 0
内存在 25 秒内从 32 MiB 增长到 1 GiB。
G1 快速内存使用增长
如果内存使用量增长非常快,JVM 人体工程学会忽略 Xms 缩放步骤,并根据其内部自适应优化算法更快地保留 RAM。因此,相对于快速的实际使用量(蓝色)增长,我们看到 JVM(橙色)的 RAM 分配要快得多。因此,使用 G1 我们是安全的,即使在负载峰值的情况下也是如此。
java -XX:+UseG1GC -Xmx2g -Xms32m -jar app.jar 10
在 4 个周期内,内存在 90 秒内从 32 MiB 增长到 1 GiB。
有时 JVM 人体工学需要几个周期才能找到最佳的 RAM 分配算法。
G1 中等内存使用增长
正如我们所见,JVM 在第 4th 周期调整了符合人体工程学的 RAM 分配,使垂直扩展对于可重复周期非常有效
java -XX:+UseG1GC -Xmx2g -Xms32m -jar app.jar 100
内存从 32 MiB 增长到 1 GiB,增量时间增长大约 300 秒。非常灵活和高效的资源扩展,满足我们的期望——令人印象深刻。
G1 内存使用增长缓慢
正如您所看到的,橙色区域(保留 RAM)随着蓝色区域(实际使用)的增长而缓慢增加。因此不会过度使用或不必要地保留内存。
用于提高 Java 应用程序性能的流行 JVM 配置通常会阻碍有效垂直扩展的能力。因此,您需要在对您的应用程序最重要的属性的优先级之间进行选择。
许多广泛使用的设置之一是激活 Aggressive Heap 以尝试最大限度地利用堆的物理内存。让我们分析一下使用此配置时会发生什么。
java -XX:+UseG1GC -Xmx2g -Xms2g
或者
java -XX:+UseG1GC -Xmx2g -XX:+AggressiveHeap
G1 激进堆
正如我们所见,保留堆(橙色)是恒定的,不会随时间变化,因此容器中的 JVM 没有垂直扩展。即使您的应用程序只使用了可用 RAM 的一小部分(蓝色),其余部分也无法与其他进程或其他容器共享,因为它已完全分配给 JVM。
因此,如果您想垂直扩展您的应用程序,请确保未启用积极堆(参数应为 -XX:-AggressiveHeap
),也不要将 -Xms
定义为 -Xmx
(例如,不要声明 -Xmx2g -Xms2g
)。
Parallel 是一种高吞吐量的 GC,在 JDK8 中默认使用。同时,它不适合内存收缩,不适合灵活的垂直缩放。为了确认这一点,让我们用我们的示例应用程序运行一个测试:
java -XX:+UseParallelGC -Xmx2g -Xms32m -jar app.jar 10
并行垃圾收集器
正如我们所见,未使用的 RAM 不会释放回操作系统。具有并行 GC 的 JVM 会永远保留它,甚至忽略显式的 Full GC 调用。
因此,如果您想根据应用程序负载从垂直扩展中获益,请将并行更改为 JDK 中可用的收缩 GC。它会将所有活动对象打包在一起,删除垃圾对象,取消提交并将未使用的内存释放回操作系统。
Serial 和 ConcMarkSweep 也在缩小垃圾收集器,并且可以垂直扩展 JVM 中的内存使用。但与 G1 相比,它们需要 4 个 Full GC 周期来释放所有未使用的资源。
让我们看看这两个垃圾收集器的测试结果:
java -XX:+UseSerialGC -Xmx2g -Xms32m -jar app.jar 10
串行垃圾收集器
java -XX:+UseConcMarkSweepGC -Xmx2g -Xms32m -jar app.jar 10
ConcMarkSweep 垃圾收集器
从 JDK9 开始,可以使用新的 JVM 选项 -XX:-ShrinkHeapInSteps 加速内存释放,该选项会在第一个 Full GC 周期后立即关闭已提交的 RAM。
Shenandoah 是垃圾收集器中一颗冉冉升起的新星,已经被认为是 JVM 垂直扩展的最佳未来解决方案。
与其他方法相比,主要区别在于无需调用 Full GC 即可异步收缩(取消提交并将未使用的 RAM 释放给操作系统)的能力。 Shenandoah 几乎可以在检测到空闲内存后立即压缩活动对象、清理垃圾并将 RAM 释放回操作系统。并且省略 Full GC 的可能性导致消除相关的性能下降。
让我们看看它在实践中是如何工作的:
java -XX:+UseShenandoahGC -Xmx2g -Xms32m -XX:+UnlockExperimentalVMOptions -XX:ShenandoahUncommitDelay=1000 -XX:ShenandoahGuaranteedGCInterval=10000 -jar app.jar 10
这里我们添加了一些在 Shenandoah 中可用的额外参数:
-XX:+UnlockExperimentalVMOptions
– 需要启用下面列出的取消提交选项-XX:ShenandoahUncommitDelay=1000
——垃圾收集器将开始取消提交未使用时间超过此时间(以毫秒为单位)的内存。请注意,当应用程序想要取回内存时,将延迟设置得太低可能会导致分配停顿。在实际部署中,建议将延迟设置为大于 1 秒-XX:ShenandoahGuaranteedGCInterval=10000 -
这保证了 GC 周期在规定的时间间隔内(以毫秒为单位)Shenandoah 垃圾收集器
Shenandoah 非常有弹性,只分配必要的资源。它还会压缩已使用的 RAM(蓝色)并将未使用的保留 RAM(橙色)动态释放回操作系统,而无需昂贵的 Full GC 调用。请注意此 GC 是实验性的,因此您对稳定性的反馈将对其创建者有所帮助。
Java 不断完善并适应不断变化的需求。因此,目前它的 RAM 胃口不再是传统应用程序的微服务和云托管的问题,因为已经有合适的工具和方法来适当地扩展它、清理垃圾并释放所需进程的资源。通过巧妙配置,Java 可以对所有范围的项目都具有成本效益——从云原生初创公司到遗留企业应用程序。
标签2: Java教程地址:https://www.cundage.com/article/jcg-minimize-java-memory-usage-right-garbage-collector.html