首先我們知道:JVM發生記憶體錯誤的型別
1、堆記憶體洩漏:OutOfMemory:Java heap space
此種記憶體洩漏,增加記憶體,只能暫時解決問題,並不能根治問題。必須要優化程式碼,一定是程式碼的問題:排查堆中的大量物件,就會發現,這些物件都被引用,物件不能及時被回收,導致超出了堆的設定最大記憶體。
2、老年代記憶體洩漏:OutOfMemoryError:PermGen space
類名、訪問修飾符、欄位描述、方法描述等,所佔空間大於永久代最大值,就會出現,一般都是初始化記憶體的時候,空間太小,解決辦法是擴大空間
3、棧記憶體洩漏:StackOverflowError
程式碼執行時,程式碼進棧,此刻如果棧的記憶體不足以容納要進棧的程式碼的大小空間,那麼久會報該錯誤。可以通過設定棧的空間大小。一般通過:-Xss設定執行緒的大小來解決。比如:-Xss256m
下面進入如何檢測jvm記憶體情況的實際操作:
1、監控JVM的GC情況:
jstat -gcutil pid 2000 20 (只需要看O,如果達到100%,並且長期處於100%,則代表老年代記憶體不足)
pid:服務程式的pid,可通過:ps -ef | grep java 來檢視java的服務程式pid
2000:每隔多少秒進行監控一次。這裡是2秒
20: 總共獲取20次
E:eden區
O:老年代
P:永久代
YGC:新生代的GC次數
YGCT:當前統計的YGC一共花費的時間(毫秒)
FGC:fullGC老年代的GC次數
FGCT:當前統計的FGC一共花費的時間(毫秒)
GCT:YGC+FGC
2、檢視jvm配置資訊
jmap -head pid //可以看到java程式的堆的配置資訊,各區的空間大小和配置資訊
3、檢視jvm中類和物件的佔用情況
jmap -histo 5279 | head -20 //檢視jvm中各個類的例項數、佔用記憶體數量以及類的全名
4、上面使我們的基本命令使用,那麼我們怎麼預測或者說記憶體洩漏會有什麼徵兆?
1、tps出現大幅波動,並慢慢降低,甚至降為0,響應時間隨之波動,慢慢升高
2、通過jstat命令看到,Jvm中Old區不斷增加,FullGC非常頻繁,對應的FullGC消耗的時間也不斷增加
3、通過jconsole/jvisualvm可以看到,堆記憶體曲線不斷上升,接近上限時,變成一條直線
4、日誌報錯java.lang.OutOfMemoryError: Java heap space
5、記憶體洩露怎麼定位?
(1、確定徵兆: 現象方面,Java程式丟擲OOM異常,分析屬於那種異常,是正常的記憶體資源耗盡還是記憶體洩漏。
(2、監控jvm的GC情況: jstat -gcutil pid 1000 100 (只需要看O,如果達到100%,並且長期處於100%,則代表老年代記憶體不足) 如果有大量的FGC就要查詢是否有記憶體洩漏的問題了
(3、通過jmap命令:jmap -histo pid | head -20,檢視當前堆記憶體中例項數和佔用記憶體最多的前20個物件
(4、通過jvisualvm分析定位:進行遠端堆dump,然後把dump檔案下載下來,用jvisualvm開啟進行分析,可以看到更直觀的jvm中物件的資訊
6、下面是進行最後的JVM引數調優:
vm常用引數
---------------------------------------
堆記憶體 = 年輕代+老年代
年輕代 = Eden+Survivor
Survivor = From Space+To Space
---------------------------------------
年輕代 = Eden+From Space+To Space
堆記憶體=Eden+From Space+To Space+老年代
====================================
-Xms2048m:初始堆大小,建議<實體記憶體的1/4,預設值為實體記憶體的1/64
-Xmx2048m:最大堆大小,建議與-Xms保持一致,預設值為實體記憶體的1/4
-Xmn512m:新生代大小,建議不超過堆記憶體的1/2-Xss256k,執行緒堆疊大小,建議256k
-XX:PermSize=256m:永久代初始值,預設值為實體記憶體的1/64
-XX:MaxPermSize=256m:永久代最大值,預設值為實體記憶體的1/4
-XX:SurvivorRatio=8:年輕帶中Eden區和Survivor區的比例,預設為8:1,即Eden(8),FromSpace(1),ToSpace(1)
-XX:MaxTenuringThreshold=15:晉升到老年代的物件年齡,每個物件堅持過一次MinorGC後物件年齡+1,預設值是15,年齡超過15進入到老年代,該引數在序列GC時有效-
XX:PretenureSizeThreshold=3145728:單位位元組,只對Serial和ParNew兩款收集器有效,新生代採用Parallel Scavenge GC時無效,大於這個值的物件直接在老年代進行分配
7、引數設定在哪裡?
一般來說,一個伺服器可能有多個java服務進行,對吧?是很多伺服器都會有這種情況,那麼問題來了,那我要是統一在jvm配置檔案修改jvm引數,那麼豈不是所有服務都是這個引數?所以有沒有其他辦法?
有!!!!那就是在java程式啟動命令設定。
nohup java ${JAVA_OPT} -jar xxx.jar 2>&1&
JAVA_OPT的引數:裡面引數自行看要設定什麼,以及具體的引數值
JAVA_OPT="-server -Xms256m -Xmx256m -Xmn512m -XX:MetaspaceSize=64m -XX:MaxMatespaceSize=256m"