內存監視手段及各區域內存溢出解決

引言

本文僅關注一些常見的虛擬機內存監視手段,以及JVM運行時數據區各個部分內存溢出的發生和對應的解決方案,總體來說屬於概括性總結,涉及相對不是很深入,目的是讓自己和其它初學者有一個框架性、概念性的瞭解,當遇到問題時有跡可循、不至於不知所措。


一、虛擬機內存監視手段

虛擬機常出現的問題包括:內存泄露、內存溢出、頻繁GC導致性能下降等,導致這些問題的原因可以通過下面虛擬機內存監視手段來進行分析,具體實施時可能需要靈活選擇,同時藉助兩種甚至更多的手段來共同分析。

比如GC日誌可以分析出哪些GC較爲頻繁導致性能下降、是否發生內存泄露。jstat工具和GC日誌類似,同樣可以查看GC情況、分析是否發生內存泄露。判斷髮生內存泄露後,可以通過jmap工具和MAT等分析工具的結合查看虛擬機內存快照,分析發生內存泄露的原因。內存溢出快照可以分析出內存溢出發生的原因等。


GC日誌記錄

將JVM每次進行GC的情況記錄下來,通過觀察GC日誌可以看出來GC的頻度、以及每次GC都回收了哪些區域的內存,根據這些信息爲依據來調整JVM相關設置,可以減少Minor GC的頻率以及Full GC的次數,還可以判斷是否有內存泄露發生。

下面是常見的GC日誌輸出參數:

u  -verbose.gc:顯示GC的操作內容。打開它,可以顯示最忙和最空閒收集行爲發生的時間、收集前後的內存大小、收集需要的時間等。

u  -XX:+printGCdetails:詳細瞭解GC中的變化。

u  -XX:+PrintGCTimeStamps:瞭解垃圾收集發生的時間,自JVM啓動以後以秒計量。

u  -XX:+PrintHeapAtGC:瞭解堆的更詳細的信息。

u  -Xloggc:[file]:將GC信息輸出到單獨的文件中


jstat:虛擬機統計信息監控工具

實時監視虛擬機運行時的類裝載情況、各部分內存佔用情況、GC情況、JIT編譯情況等。

例:每隔250ms查詢一次進程2211的垃圾收集情況,查詢50次

步驟①:jps列出本機所有運行的jvm實例,獲取jvm的pid

步驟②:jstat實時監控gc情況,jstat –gc 2211 250 50

其他參數包括:

         -class監視類裝載、卸載數量、總空間以及類裝載所耗費時間

         -gccapacity監視內容與-gc相同,輸出主要關注堆各個區域用到的最大、最小空間

         -gcutil監視內同與-gc相同,輸出主要關注堆各個區域已使用空間所佔總空間百分比

         -gcnew監視新生代GC情況

         -gcold監視舊生代GC情況


jmap:虛擬機內存映像工具

jmap工具可以讓運行中的JVM生成Dump文件,當JVM內存出現問題時可以通過jmap生成快照,分析整個堆,主要經歷兩個步驟:

步驟1:jps列出本機所有運行的jvm實例,獲取jvm的pid

步驟2:使用jmap命令將指定JVM快照導出爲dump文件

jmap -dump:format=b,file=path/heap.bin PID   

獲得JVM快照的dump文件之後,可以通過MAT工具進行分析。

MAT(MemoryAnalyzer Tool)工具是eclipse的一個插件,使用起來非常方便,尤其是在分析大內存的dump文件時,可以非常直觀的看到各個對象在堆空間中所佔用的內存大小、類實例數量、對象引用關係、利用OQL對象查詢,以及可以很方便的找出對象GC Roots的相關信息,最吸引人的是能夠快速爲開發人員生成內存泄露報表,方便定位和分析問題。

除此之外,jmap還可以查詢finalize執行隊列、java堆和持久代的詳細信息,比如空間使用率,當前使用的是哪種收集器等。


內存溢出快照生成

通過設置JVM參數,可以讓虛擬機發生OutOfMemoryError(OOM)內存溢出時自動生成dump文件,通過分析dump文件查看內存使用情況可以找到內存溢出發生的原因:

-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/path/to/heap/dump

獲得JVM快照的dump文件之後,可以通過MAT工具進行分析。


二、運行時數據區內存溢出

JVM運行時數據區分爲以下幾個部分:

其中方法區和堆是所有工作線程共享的,而棧、程序計數器和本地方法棧是線程私有的。


注:圖片轉自網絡


1.程序計數器

作用:指向當前線程下一條需要執行的字節碼指令的地址

內存溢出:不會發生


2.虛擬機棧

作用:由棧幀組成、每個棧幀代表一次方法調用,其包含存儲變量表、操作數棧和方法出口三個部分,方法執行完成後該棧幀將被彈出。

內存溢出:StackOverflowError和OutOfMemoryError。

溢出原因:

StackOverflowError:如果請求的棧的深度大於虛擬機所允許的深度,將會拋出這個異常,如果使用虛擬機默認參數,一般達到1000到2000這樣的深度沒有問題。

OutOfMemoryError:因爲除掉堆內存和方法區容量,剩下的內存由虛擬機棧和本地方法棧瓜分,如果剩下的內存不足以滿足更多的工作線程的運行、或者不足以拓展虛擬機棧的時候,就會拋出OutOfMemoryError異常。

解決方法:

針對StackOverflowError:

1.      首先棧溢出會輸出異常信息,根據信息查看對應的方法調用是否出現無限調用、或者棧幀過大等代碼邏輯上的問題,通過修改代碼邏輯解決;

2.      如果確確實實需要更大的棧容量,可以檢查並調大棧容量:-Xss16m。

針對OutOfMemoryError:

1.      首先檢查是否創建過多的線程,減少線程數

2.      可以通過“減少最大堆容量”或“減少棧容量”來解決。


3.本地方法棧

作用:與虛擬機棧唯一的不同是虛擬機棧執行的是java方法,而本地方法棧執行的是本地的C/C++方法

內存溢出:StackOverflowError和OutOfMemoryError

溢出原因:同虛擬機棧

解決方法:同虛擬機棧


4.堆

作用:所有線程共享,存放對象實例

內存溢出:OutOfMemoryError:Java heap space

溢出原因:堆中沒有足夠內存完成實例分配,並且無法繼續拓展時

解決方法

1.內存泄露檢查:首先通過“內存溢出快照 + MAT等分析工具”,分析是否存在內存泄露現象,檢查時可以懷疑的點比如集合、第三方庫如數據庫連接的使用、new關鍵字相關等。

2.如果沒有內存泄露,那麼就是內存溢出,所有對象卻是都還需要存活,這個時候就只能調大堆內存了:-Xms和-Xmx。


5.方法區

作用:所有線程共享,存放已加載的class信息、常量、靜態變量和即時編譯後的代碼

內存溢出:OutOfMemoryError:PermGen space

溢出原因:方法區沒有足夠內存完成內存分配存放運行時新加載的class信息

解決方法

1. 內存泄露檢查:檢查是否加載過多class文件(jar文件),或者重複加載相同的class文件(jar文件)多次

2. 通過-XX:PermSize=64M -XX:MaxPermSize=128M改大方法區大小


6.運行時常量池

作用:方法區的一部分,存放常量

內存溢出:OutOfMemoryError:PermGen space

溢出原因:方法區沒有足夠的內存完成內存分配,存放運行時新創建的常量,比如String類的intern()方法,其作用是如果常量池已經包含一個相同的字符串,則返回其引用,否則將此String對象包含的字符串添加到常量池中。

解決方法

1. 內存泄露檢查:檢查是否創建過多常量

2. 通過-XX:PermSize=64M -XX:MaxPermSize=128M改大方法區大小


7.直接內存

作用:不屬於JVM運行時數據區,也不是虛擬機規範中定義的內存區域,JDK1.4引入的NIO中包含通道Channel和緩衝區Buffer,應用程序從通道獲取數據是先經過OS的內核緩衝區,再拷貝至Buffer,因爲比較耗時,所以Buffer提供了一種直接操作操作系統緩衝區的方式,即ByteBuffer.allocateDirector(size),這個方法返回DirectByteBuffer應用就是指向這個底層存儲空間關聯的緩衝區,即直接內存(native memory),或者叫堆外內存

內存溢出:OutOfMemoryError

溢出原因:JVM所需內存 + 直接內存 > 機器物理內存(或操作系統級限制),無法動態拓展

判斷方法:內存泄露檢查:例如內存佔用較高,機器性能驟降,但是通過GC信息或者jstat發現GC很少,通過jmap獲得快照分析後也沒發現什麼異常,而程序中又直接或者間接地用到了NIO,那麼和可能就是直接內存泄露了。

解決方法:分析NIO相關的程序邏輯解決。

發佈了37 篇原創文章 · 獲贊 20 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章