优化 AWS ECS 的 Java 堆设置

优化 aws ecs 的 java 堆设置

我们在 AWS Elastic Container Service(ECS) Fargate 上运行多个 Java 服务 (Corretto JDK21)。每个服务都有自己的容器,我们希望使用为每个进程支付的所有可能的资源。但这些步骤可以应用于 EC2 和其他云。

服务正在运行批处理作业,延迟并不重要,我们使用并行GC(-XX:+UseParallelGC)。即使完成我们的任务,也许 G1 会更好,但这是一个需要单独研究和发布的主题。
为了使用所有可用内存,我们的 MaxHeapSize 略低于容器内存大小。但一段时间后,我们注意到两个问题,有时我们的容器因为使用太多内存而被杀死,有时我们收到 OutOfMemoryError 异常。 为了解决第一个问题,我们增加了容器内存大小和 MaxHeapSize 之间的差距,对于第二个问题,增加了容器内存作为快速解决方案,并开始查看堆转储。

堆转储显示了有趣的细节,实际堆大小低于 MaxHeapSize,并且与老一代相比,年轻代堆很小。

在互联网上搜索并没有找到关于如何针对我们的案例调整 JVM 参数的良好指南,我只找到了一些有关堆和参数描述的高级详细信息。我决定写这篇文章来描述我所做的步骤。

第一步是:

  • 打印有关参数和默认值的信息:(-XX:+PrintFlagsFinal),
  • 将InitialHeapSize设置为与MaxHeapSize相同的值(-XX:InitialRAMPercentage=100或仅将-XX:InitialHeapSize设置为与MaxHeapSize相同的值)。无论如何,我们都在为所有容器内存付费,所以为什么不从一开始就分配它呢?
  • 记录 GC 和堆信息 (-Xlog:gc*)。

Young:Old Generation默认比例为1:2,同时只使用部分Young Generation进行GC。启动后,JVM 按预期分配了所有内存,但一段时间后,它开始将年轻代堆大小减少到几乎几兆字节。所以一段时间后我们只使用了 2/3 的可用内存。
经过一番挖掘,我发现了一个禁用自适应策略的参数(-XX:-UseAdaptiveSizePolicy),它有所帮助,堆停止减少,垃圾收集之间的间隔增加了一个数量级甚至更多。 GC 消耗的时间也有所增长,但增幅不大。

下一步是找到容器内存大小之间的最佳差距。默认情况下,即使InitialRAMPercentage=100,JDK也只是分配内存而不使用它,因此它不会被映射。 Linux 允许分配比物理内存更多的虚拟内存。当内存实际映射(JDK 写入它)时,容器稍后会失败。 -XX:+AlwaysPreTouch 更改此行为。不幸的是,有些内存仍然没有映射,但 OOM 终止发生得更快。经过几次尝试后,我得出了下一个公式“容器内存大小 - 1024MB”(对于具有 8GB 或更多内存的容器)。例如,对于 8192 容器内存大小,我们使用 -XX:MaxHeapSize=7168m。

为了进一步优化,我们正在考虑更改 -XX:NewRatio 以减少年轻代大小并减少 GC 时间。但这取决于对象在应用程序中的生命周期。
正如我之前提到的,我还没有找到任何好的指南来详细解释参数(我发现最好的是 vm-options-explorer)和调整步骤。如果您能分享您的知识和成果,那就太好了。

以上就是优化 AWS ECS 的 Java设置的详细内容,更多请关注其它相关文章!