1. 程式人生 > 其它 >如何規避容器內做Java堆dump導致容器崩潰的問題

如何規避容器內做Java堆dump導致容器崩潰的問題

寫在前邊

最近公司生產環境的容器雲上出了個性能問題,為了做效能分析,使用 JDK 自帶的 jmap 收集堆dump,出現了記憶體溢位導致了容器崩潰。

本篇文章將帶你探究,如何規避容器內做堆 dump 導致容器崩潰的問題。適用於低於 Java 8 update 191版本的 JDK。

分析容器崩潰原因

確認容器崩潰的根本原因

毋庸置疑的是容器的記憶體佔用超過了容器的限制,被 k8s 或 docker kill 掉了。

為什麼會超過容器的限制呢?

有兩種可能:

  • Java 應用服務記憶體仍在攀升,就算不操作也會崩潰
  • jmap 收集堆dump時佔用記憶體所致

根據容器雲平臺的運維人員提供的監控截圖,基本確認是第二種情況。

Ps: GMT時間+8小時即北京時間,18點左右出現容器記憶體急劇攀升的情況,而此時正在收集堆dump

為什麼 jmap 會申請超過容器限制的記憶體呢?

首先,我們需要知道 jmap 本身是啟動了另一個 JVM 來收集問題應用的 JVM堆的資訊的。提到 JVM 執行在容器裡會出現的問題,第一個就應該想到 JVM 對容器環境的支援問題,即低於 Java 8 update 191 版本的 JDK 會直接讀到物理節點的記憶體,從而根據物理節點去申請記憶體,直接導致了容器崩潰。

本次環境就是 OpenJDK 8 低於 191 的版本,所以原因找到了。

解決方案

參考了兩個版本的 jmap 官方文件,發現有個可以配置 jmap 所在 JVM 的引數 -Jflag

JDK 7

-J<flag>
    Passes <flag> to the Java virtual machine on which jmap is run.

JDK 8

-Jflag
    Passes flag to the Java Virtual Machine where the jmap command is running.

這裡筆者確認了下這個flag引數最終傳遞到的是 jmap 所在的 JVM 中,而不是應用服務 JVM

所以方案就比較簡單了,根據容器除應用服務 JVM 堆及元空間記憶體外的可用記憶體,設定個合理的JVM即可。

示例:容器限制 32G 記憶體,應用服務堆佔用 24G,元空間佔用 2G,則可用記憶體在 6G以下。給容器預留 1G 記憶體防止其崩潰,可以設定 jmap 使用 5G。

命令格式:jmap [輸出dump檔案配置] [設定jmap虛擬機器引數] [程序號]

jmap -dump:format=b,file=/deployments/heap.hprof -J-Xmx5g 1

參考文件:

https://docs.oracle.com/javase/7/docs/technotes/tools/share/jmap.html

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jmap.html

本文同步發表在筆者部落格園[https://www.cnblogs.com/hellxz/]與CSDN[https://blog.csdn.net/u012586326],禁止轉載。