我們在 AWS Elastic Container Service(ECS) Fargate 上執行多個 Java 服務 (Corretto JDK21)。每個服務都有自己的容器,我們希望使用為每個進程支付的所有可能的資源。但這些步驟可以應用於 EC2 和其他雲端。
服務正在運行批次作業,延遲並不重要,我們使用並行GC(-XX:UseParallelGC)。即使完成我們的任務,也許 G1 會更好,但這是一個需要單獨研究和發布的主題。
為了使用所有可用內存,我們的 MaxHeapSize 略低於容器內存大小。但一段時間後,我們注意到兩個問題,有時我們的容器因為使用太多記憶體而被殺死,有時我們收到 OutOfMemoryError 異常。 為了解決第一個問題,我們增加了容器記憶體大小和 MaxHeapSize 之間的差距,對於第二個問題,增加了容器記憶體作為快速解決方案,並開始查看堆轉儲。
堆轉儲顯示了有趣的細節,實際堆大小低於 MaxHeapSize,與老一代相比,年輕代堆很小。
在網路上搜尋並沒有幫助找到如何針對我們的案例調整 JVM 參數的良好指南,我只找到了一些有關堆和參數描述的高級細節。我決定寫這篇文章來描述我所做的步驟。
第一步是:
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)和調整步驟。如果您能分享您的知識和成果,那就太好了。
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3