非堆記憶體洩漏JVM
我在執行在 oracle java 虛擬機上的 Ubuntu 伺服器上設置了 glassfish v4.0,並且 jvm 程序駐留記憶體大小(通過“top”命令獲得)增長,直到 jvm 沒有記憶體來創建新執行緒。
是)我有的:
- 具有 1Gb 記憶體和 1.4GHz 處理器 (1Core) 的 VPS 伺服器
- Ubuntu 伺服器 12.04
- Java(TM) SE 執行時環境 (build 1.7.0_51-b13)
- Java HotSpot(TM) 64 位伺服器 VM(內部版本 24.51-b03,混合模式)
- Glassfish v4.0 執行我的 Java EE webapp
- VM 使用以下參數執行 -XX:MaxPermSize=200m -XX:PermSize=100m -XX:Xmx=512m (如果相關,我可以添加所有參數)
有什麼問題:
Ram 使用量(res 記憶體)一直在增長,取決於每小時 10-100m 的使用量,直到 jvm 無法分配本機記憶體。
我試過什麼:
- 我已經降低了最大堆空間,這只會在 jvm 崩潰之前節省時間
- 我已經附加了 plumbr ( https://portal.plumbr.eu/ ),它沒有檢測到堆中的任何記憶體洩漏
- 我還將最大燙髮大小設置為較低的值。
我想讓我的 JVM 保持穩定,因為我測量堆空間 + perm gen 只需要 400-600 mb,而“top”命令顯示 java 程序記憶體增長到 850mb,然後自行終止。我知道 JVM 需要比 perm space 和 heap 更多的記憶體,但是你認為我仍然給 heap space 和 perm gen 提供了太多記憶體嗎?任何幫助或指導將不勝感激。
日誌輸出:http ://pakers.lv/logs/hs_err_pid970.log 所有JVM flgas:http ://pakers.lv/logs/jvm_flags.txt
更新
我還嘗試了什麼(基於建議和我自己的發現):
- 我已將堆空間減少並固定到 256m,然後在系統仍然穩定時增加,我注意到我的系統可以承受的最大堆空間是 512m 和 128m perm gen 空間。(-Xmx512m,-Xms512m,-XX:PermSize=128m,-XX:MaxPermSize=128m)
- 減少 java 執行緒大小 -Xss256k,我無法將其減少到 218k 以下(jvm 不會啟動)
- 添加 -D64 使 jvm 在 64 位模式下執行
- 添加 -XX:+AggressiveOpts(啟用性能優化)、-XX:+UseCompressedOops(減少堆空間記憶體使用)、-server 標誌以在伺服器模式下啟動 jvm
- 由於我的堆空間大小非常有限,我修改了 NewRatio 以擁有更大的終身代(堆空間的 1/3)-XX:NewRatio=3
- 為 GC 添加了診斷選項,以便我可以檢查 OOM 錯誤 -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+HeapDumpOnOutOfMemoryError -Xloggc:/home/myuser/garbage.log
目前狀態 通過這些更改,我終於限制了我的目標 java 程序的常駐記憶體(RAM 使用)。在我的情況下,512m 堆空間 + 128m perm gen 空間導致 java 程序的駐留記憶體約為 750m,這是穩定的。即使我仍然有記憶體問題 - 堆記憶體不時變滿並由於持續的垃圾收集而導致 Web 應用程序凍結,但作業系統不會終止該程序。所以我現在需要增加系統的可用記憶體 (RAM) 或檢查堆使用情況並降低我的應用程序的佔用空間。由於我的 webapp 是基於 Java EE(使用 EJB)的,因此我可能無法顯著減少它。無論如何,感謝您的建議,如果有任何其他建議,請隨時分享。
鑑於您分享的內容,有幾種可能性,例如:
- 一個洩漏的 JNI 庫,或者,
- 執行緒創建洩漏,或
- 洩漏的動態程式碼代理(perm-gen洩漏),
但我只能猜測,因為您沒有提供任何日誌輸出,或者指示 JVM 是否拋出
OutOfMemoryException
(OOM),或者是否遇到了其他錯誤。您也沒有提到正在使用什麼垃圾收集器,但如果上面顯示的標誌是唯一使用的 JVM 選項,那就是 CMS 收集器。第一步是通過添加這些標誌使垃圾收集器的操作可觀察:
-XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+HeapDumpOnOutOfMemoryError -Xloggc:/path/to/garbage.log
如果它確實是 OOM,你可以使用 VisualVM 或類似工具分析堆轉儲。我還使用 VisualVM 通過 JMX 現場監控 GC 操作。通過這些 JVM 標誌可以啟用對 JVM 內部的可見性:
-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=4231 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
其他資源:
- http://www.oracle.com/technetwork/java/javase/memleaks-137499.html
- http://javabook.compuware.com/content/memory/problem-patterns/memory-leaks.aspx
- https://visualvm.java.net/heapdump.html
- http://www.marcsturlese.com/2009/05/09/analyzing-java-heaps-with-jmap-and-jhat/
更新
日誌確實有幫助。謝謝你。該特定日誌顯示它在堆增長到配置的最大值之前就耗盡了物理記憶體。它試圖 malloc ~77M 並且只剩下 ~63M 物理空間:
本機記憶體分配 (malloc) 未能為送出保留記憶體分配 77873152 字節。
..
/proc/meminfo: MemTotal: 1018724 kB MemFree: 63048 kB
這是我要做的:
- 減少堆,使其“適合”機器。將最小和最大堆設置為相同的值,以便您可以判斷它是否會立即適合 - 如果不適合,它將不會啟動。
-Xss
您可以減小 Java 堆棧大小(我認為 64 位 Linux 的預設值是 256k。減少太多,它會在堆棧分配上開始OOM。- 重複測試。
- 當它在負載下執行一小段時間時,使用
jmap -dump:file=path_to_file <pid>
.- 應該發生以下兩種情況之一:(a)如果有洩漏,它最終會再次失敗,但 OOM 的類型應該不同,或者(b)沒有洩漏使得 GC 會更加努力地工作並且你完成了。鑑於您之前嘗試過,前一種情況很可能,除非您減小的最大尺寸也不適合。
- 如果它確實 OOM,請比較兩個轉儲以查看使用
jhat
或其他一些堆分析器增長的內容。祝你好運!