原文連接:http://www.cnblogs.com/yjmyzz/p/7478266.htmlphp
1、jmap找出佔用內存較大的實例html
先給個示例代碼:java
+ View Codegit
List中放了1w個Person對象的實例,先把這段程序跑起來github
javac OOMTest.javaspring
java OOMTest數組
而後再開一個窗口,jps -l 找出該程序的pid瀏覽器
而後執行 jmap -histo:live 7320 (注:若是輸出內容太多,只想看排名前10的,能夠加 | head -10)服務器
輸出結果,會按內存使用量,從大到小依次把對象的實際個數,佔用內存數量(字節數)打印出來,最後還會輸出彙總信息併發
以上面的示例來講,OOMTest$Person這個類的實例數爲10000個,總共佔用240000字節(注:即每一個實例24字節),這個程序總佔用內存數爲725464字節,約:0.69M。
另外還有一些[C,[B這類class name,大概意思爲:
[C is a char[]
[S is a short[]
[I is a int[]
[B is a byte[]
[[I is a int[][]
[C對象每每跟String有關,String其內部使用final char[]數組來保存數據的
constMethodKlass/ methodKlass/ constantPoolKlass/ constantPoolCacheKlass/ instanceKlassKlass/ methodDataKlass
與Classloader相關,常駐與Perm區。
2、找出某個java應用打開的句柄數及線程數
ll /proc/{pid}/fd | wc -l 查看打開的句柄數
ll /proc/{pid}/task | wc -l 查看線程數
3、jmap 查看堆內存的各項配置
jmap -heap pid 能夠看到相似下面的輸出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
注:5-16行是堆內存的主要配置,這些參數均可以經過 java -XX:參數名=參數值 來調整其大小,好比:
java -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=80 -Xmx100m -XX:MetaspaceSize=50M -XX:NewRatio=3 將影響MinHeapFreeRatio、MaxHeapFreeRatio、MaxHeapSize、MetaspaceSize、NewRatio的值
注意下NewRatio,這個值指的 老年代(Old Generation): 新生代(Young Generation)的比值,上面設置成3,因此OldSize爲75m,而NewSize爲25m,參考下圖:
注:這是jdk7的示意圖,jdk8中Permanent Generation被去掉了,新加入了Metaspace區,但這個區別不影響對 新生代、老生代的理解。
新生代(Young Generation)又能夠細分爲eden、s0、s1 三大塊。
java7與java8的內存變化,大體如上圖。
SurvirorRatio這個要難算一點,按Oracle官網的解釋:https://docs.oracle.com/cd/E19159-01/819-3681/abeil/index.html ,默認值是8,即:每塊survivor: eden區的大小爲1:8,換句話說 s0 = s1 = 1 / (1+1+8) = 1/10
注:雖然官網這麼解釋,可是我實際算了下,好象並非嚴格按這個比例來算的,只能大概說是這個分配比例。(結論就是:SurvirorRatio設置越大,eden區就越大)
4、找出佔用CPU最高的線程
先來一段演示代碼:
這裏面有100個線程是空轉的,另外還有一個線程BUSY THREAD在狂跑CPU。
javac OOMTest.java
java OOMTest
把程序跑起來,jps -l 找出pid,而後 top -Hp pid
能夠看到pid 16813這個對應的線程,把CPU快跑滿了,達到了98.5%
接下來,將16813轉換成16進制 ,即41ad ,而後
jstack pid | grep '41ad'
咱們就把最忙的這個線程BUSY THREAD給找出來了(注:這個技巧再次說明了,給線程取個好名字至關重要!)
tips:若是使用spring-boot的話,直接在瀏覽器裏查看/dump端點,也能夠達到相似jstack的效果。
5、jvisualvm 查看運行狀況
JDK_HOME/bin下有一個自帶的jvisualvm工具,能夠圖形化的查看GC狀況(注:要安裝插件)
java.net這個網站已經被oracle關了,因此安裝插件這裏,有點小麻煩,先到https://visualvm.github.io/pluginscenters.html 這裏找到jvisualvm對應的jdk版本號,以jdk8爲例,地址就是 https://visualvm.github.io/uc/8u131/updates.xml.gz
而後,把這個地址在Plugins裏的Settings裏改一下,而後Available Plugin這裏,就能看到可用插件了,選擇GC插件並安裝。
能夠來一段代碼,而後用jvisualvm來看下GC狀況
能夠直觀的看到Old區,Eden區,S0,S1以及Metaspace區的內存變化狀況,以上圖爲例:Old Gen區佔用內存一直在增長,表示可能有內存一直未被釋放,值得關注。
此外,還能夠看到佔用內存最多的類(即:本文最開始提到的)
也能夠查看哪些線程最忙
6、使用jstat 查看GC
雖然jvisualvm很好用,可是一般服務器是用終端連上的,沒法運行圖形化界面,並且也並不是全部應用都開啓了jmx,因此掌握jstat以命令行方式查看GC狀況也是蠻重要的
用法:jstat -gc pid 採樣間隔毫秒數,好比: jstat -gc 8544 5000,將每隔5s採樣一次pid爲8544的gc狀況
以上圖爲例:紅剪頭的地方,S0區的已用量降到0,而S1區的已用量上漲,即說明發生了Young GC,對象從S0區被遷移到了S1區。
title欄的含義以下:
S0C - 新生代中第1塊survivor 的容量(Survivor 0 Capacity),KB單位
S1C - 新生代中第2塊survivor 的容量(Survivor 1 Capacity),KB單位
S0U - 新生代中第1塊survivor 已使用空間數(Survivor 0 Used),KB單位
S1U - 新生代中第2塊survivor 已使用空間數(Survivor 0 Used),KB單位
EC - Eden區的容量(KB)
EU - Eden區已使用(KB數)
OC - Old區的容量(KB)
OU - Old區已使用(KB數)
MC - Metaspace容量(KB)
MU - Metaspace已使用KB
CCSC - 壓縮類的內存容量(KB)
CCSU - 壓縮類的已用容量(KB)
YGC - (從應用啓動算起,到採樣時的) Young GC次數
YGCT - (從應用啓動算起,到採樣時的) Young GC所用時間(秒)
FGC - (從應用啓動算起,到採樣時的) Full GC次數
FGCT - (從應用啓動算起,到採樣時的) Full GC所用時間(秒)
GCT - (從應用啓動算起,到採樣時的) Yong GC + Full GC的總時間
值得一提的是G1垃圾回收器,在大堆(>4G)時,用G1可能效果會更好,G1的開啓方法:
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
開啓後,再使用jmap -heap pid
能夠看到從默認的併發GC變成了G1.
jstat -gc pid 5000
看到S0全是0,這也是G1的一個特色,將新生代與老年代的劃分取消掉了,而是用region的新概念,把整個堆內存劃分紅一個個region,每一個region裏再分代,詳情見本文最後的參考文章。
7、導出整個jvm的dump
jmap -dump:format=b,file=文件名 [pid]
最後這個算是放大招了,把整個jvm都導出來分析,一般是其它手段都搞不定的時候,才找運維去搞這個,導出的文件體積大,並且導出時會使應用停頓。把這個文件弄到本地後,能夠用eclipse的一個插件MAT來分析,下載地址:http://www.eclipse.org/mat/downloads.php
參考文章:
Java GC系列 http://www.importnew.com/13504.html
深刻理解 Java G1 垃圾收集器 http://blog.jobbole.com/109170/
jstat Oracle官方介紹 http://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html
做者:菩提樹下的楊過
出處:http://yjmyzz.cnblogs.com 本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。