我们在 AWS Elastic Container Service(ECS) Fargate 上运行多个 Java 服务 (Corretto JDK21)。每个服务都有自己的容器,我们希望使用所有可能的资源来支付每个流程。但是这些步骤可以应用 EC2 和其他云。
服务正在运行批处理作业,延迟并不重要。我们使用并行GC(-XX:+UseParallelGC)。即使我们完成了任务,也许 G1 会更好,但这是一个需要单独研究和发布的主题。 为了使用所有可用的内存,我们 MaxHeapSize 略低于容器内存的大小。但过了一段时间,我们注意到了两个问题。有时我们的容器因为使用过多的内存而被杀死,有时我们收到它 OutOfMemoryError 异常。 为了解决第一个问题,我们增加了容器内存的大小和 MaxHeapSize 对于第二个问题,增加容器内存作为快速解决方案,并开始检查堆转储。
堆垛转储显示了有趣的细节,实际堆垛大小小于 MaxHeapSize,与老一代相比,年轻一代堆得很小。
在互联网上搜索还没有找到如何调整我们的案例 JVM 我只找到了一些关于堆和参数描述的高级详细信息。我决定写这篇文章来描述我所做的步骤。
第一步是:
立即学习“Java免费学习笔记(深入);
- 打印相关参数和默认值信息:(-XX:+PrintFlagsFinal),
- 将InitialHeapSize设置为与MaxHeapSize相同的值。(-XX:InitialRAMPercentage=100或仅将-XX:InitialHeapSize设置为与MaxHeapSize相同的值)。无论如何,我们都在为所有的容器内存付费,那么为什么不从一开始就分配呢?
- 记录 GC 和堆信息 (-Xlog:gc*)。
Young:Old Generation默认比例为1:2.同时只使用部分Young GenerationGC。启动后,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 请关注图灵教育的其他相关文章!