Java容器應用程序內存分析

在容器化應用中,由於記憶體過度使用導致的問題往往難以察覺。若使用量超過容器記憶體限制,應用程式可能會無聲無息地失敗,不留任何痕跡。

本文將探討幾種用於識別Java容器應用中記憶體消耗來源的技術。

記憶體類型

在典型的Java應用中,記憶體大致可分為堆和非堆兩類。堆記憶體可透過在啟動任何Java應用時提供相應的JVM參數來設定。

非堆記憶體包括由JVM本身或使用JNI(Java Native Interface)的應用內任何庫所使用的原生記憶體。

方法

針對堆記憶體,可使用堆轉儲分析工具進行堆轉儲並分析。其中,EclipseMAT是堆轉儲分析的最佳工具之一。

Java提供了一種機制來追蹤原生記憶體分配,即啟用原生記憶體追蹤,但這可能無法揭示所有由原生庫分配的記憶體。

Jemalloc 是一個工具,可用於追蹤由原生庫分配的記憶體。原生記憶體是使用名為 malloc 的默認記憶體分配器進行分配的。Jemalloc 是一個通用 malloc 實現,可以啟用記憶體分配追蹤。它追蹤所有原生記憶體分配並生成堆棧配置文件傾印。

這些堆棧配置文件隨後可以使用Jeprof工具進行分析。Jeprof 生成堆記憶體分配報告,突出顯示應用程式中函數使用的記憶體。

分析

以下是對一個樣本容器 Java 應用程式的記憶體分析。該應用程式載入一個樣本Tensorflow模型以啟用原生記憶體利用,並在 Docker 容器中運行。

以下是 Docker 記憶體消耗情況。顯示為 254MB。讓我們嘗試精確定位記憶體消耗的來源。

總記憶體

為了了解應用程式進程使用的總記憶體,我們可以檢查駐留集大小(RSS)。它是駐留在主記憶體或 RAM 中的總提交記憶體。有多種工具可以幫助檢查這一點,如 top、ps 或 pmap。

檢查RSS無助於精確定位使用量的根源。對於示例應用,使用以下命令,總RSS顯示為376MB。

Shell

 

ps --no-header -o rss $(pidof java)

堆分析

以下是由eclipse MAT工具生成的堆內存消耗情況。總保留堆顯示為2.2MB,遠低於Docker顯示的總內存消耗,表明大部分消耗來自非堆區域。

原生內存分析

在審查使用以下命令獲得的原生內存摘要後,總內存使用量約為99MB。然而,此數值低於總內存消耗,並未準確識別問題的根本原因。

Shell

 

jcmd $(pidof java) VM.native_memory \
    | grep -P "Total.*committed=" \
    | grep -o -P "(?<=committed=)[0-9]+(?=KB)"

堆外內存分析

使用Jemalloc和Jeprof進行的分析揭示,原生內存使用主要歸因於Tensorflow庫,總消耗約為112MB。

此洞察清晰指示了原生內存使用的來源,並可進一步調查以減少任何過度消耗。

結論

Java內存分析對於容器化應用尤為關鍵。了解應用中內存消耗的來源有助於我們理解內存需求,並通過移除不必要的消耗來降低應用成本。

在檢查內存消耗時,需精確定位所有類型的內存及其來源。堆轉儲分析能夠精確定位堆內存消耗的來源,而Jemalloc和Jeprof在精確定位原生內存消耗來源方面非常有用。

範例應用程式程式碼連結

https://github.com/parveensaini/JavaContainerMemoryAnalysis

Source:
https://dzone.com/articles/java-container-application-memory-analysis