1. 程式人生 > >JVM內存監視手段和內存溢出解決方案

JVM內存監視手段和內存溢出解決方案

max 什麽 detail pat ogg 最大堆 還需 .net 設置

引言

本文僅關註一些常見的虛擬機內存監視手段,以及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相關的程序邏輯解決。

JVM內存監視手段和內存溢出解決方案