Change Dir

          先知cd——熱愛生活是一切藝術(shù)的開始

          統(tǒng)計(jì)

          留言簿(18)

          積分與排名

          “牛”們的博客

          各個(gè)公司技術(shù)

          我的鏈接

          淘寶技術(shù)

          閱讀排行榜

          評(píng)論排行榜

          使用MAT對(duì)java內(nèi)存分析

          這是一篇閱讀MAT helper的筆記。

          Heap dump
          java進(jìn)程在特定時(shí)間的一個(gè)內(nèi)存快照。通常在觸發(fā)heap dump之前會(huì)進(jìn)行一次full gc,這樣dump出來的內(nèi)容就包含的是被gc后的對(duì)象。

          dump文件包含的內(nèi)容:

          1,全部的對(duì)象:類,域,原生值和引用;

          2,全部的類:classloader,類名,超類,靜態(tài)域;

          3,GC root:被JVM定義的可觸達(dá)的對(duì)象;

          4,線程棧和本地變量:線程的call stack,本地對(duì)象每幀的信息。

          dump文件不包含內(nèi)存的分配信息,因此無法查詢誰創(chuàng)建了哪個(gè)對(duì)象這樣的信息。

          Shallow heap是一個(gè)對(duì)象占用的內(nèi)存空間,一個(gè)對(duì)象需要32或者64bits。

          Retained set of XX在被jvm gc回收后被remove的一組object。

          Retained heap of X是在retained set of X中的所有對(duì)象的shallow heap size的和。換句話說就是保持X活著需要的內(nèi)存空間。

          通俗的講,shallow heap是一個(gè)對(duì)象在內(nèi)存中的實(shí)際空間,而retained heap是一個(gè)對(duì)象被gc回收后內(nèi)存釋放出來的空間。

           


           

          這張圖可以看懂什么是leading set什么是retained set

           

          Dominator tree:定義一個(gè)對(duì)象x dominate 對(duì)象y,當(dāng)每一條從root開始到y的路徑都經(jīng)過x。說白了就是只要有y對(duì)象的存活,那么一定會(huì)有一個(gè)x對(duì)象。Dominator tree就是將對(duì)象引用圖轉(zhuǎn)換成的樹形結(jié)構(gòu)。幫助發(fā)現(xiàn)在對(duì)象間保持alive的依賴,同時(shí)也能識(shí)別出retained內(nèi)存的最大的chunk。Immediate dominator x of y是離y最近的dominator。

          Dominator tree有幾個(gè)屬性:

          1,對(duì)象x的子樹包含的對(duì)象(x dominate的對(duì)象集),代表了xretained set

          2,如果xyimmediate dominator,那么ximmediate dominator同樣dominate y,以此類推;

          3,dominate tree中的邊不代表對(duì)象引用圖里對(duì)應(yīng)的邊,并非嚴(yán)格的直接的對(duì)象引用。

           


           

          這張圖反應(yīng)了一個(gè)對(duì)象引用圖轉(zhuǎn)換成dominator tree的示例。

           

          Gc root:一個(gè)gc根就是一個(gè)對(duì)象,這個(gè)對(duì)象從堆外可以訪問讀取。以下一些方法可以使一個(gè)對(duì)象成為gc根。

          1,System class:被bootstrap或者system類加載器加載的類,比如rt.jar里的java.util.*;

          2,JNI localnative代碼里的local變量,比如用戶定義的JNI代碼和JVM的內(nèi)部代碼;

          3JNI globalnative代碼里的global變量;

          4,Thread block:當(dāng)前活躍的線程block中引用的對(duì)象;

          5Thread:已經(jīng)啟動(dòng)并且沒有stop的線程;

          6,busy monitor:被調(diào)用了wait()或者notify()或者被synchronized同步的對(duì)象,如果是synchronized方法,那么靜態(tài)方法指的類,非靜態(tài)方法指的是對(duì)象;

          7,java locallocal變量,比如方法的入?yún)⒑头椒▋?nèi)創(chuàng)建的變量;

          8,native stacknative代碼里的出入?yún)?shù),比如file/net/IO方法以及反射的參數(shù);

          9finalizable:在一個(gè)隊(duì)列里等待它的finalizer 運(yùn)行的對(duì)象;

          10,unfinalized:一個(gè)有finalize方法的對(duì)象,還沒有被finalize,同時(shí)也沒有進(jìn)入finalizer隊(duì)列等待finalize;

          11,unreachable:不會(huì)被觸碰到的對(duì)象,在MAT里被標(biāo)記為root用來retain object,否則是不會(huì)在分析中出現(xiàn)的;

          12,java stack framejava棧幀包含了本地變量,當(dāng)dump被解析時(shí)且在preferences里設(shè)置過把棧幀當(dāng)做對(duì)象,這時(shí)才會(huì)產(chǎn)生;

          13,unknown:位置的root類型。

           

          接下來是一些獲取dump的方法:

          1,在oom時(shí)dumpJVM參數(shù):-XX:+HeapDumpOnOutOfMemoryError

          2,交互式環(huán)境下dump

          1JVM參數(shù):-XX:+HeapDumpOnCtrlBreak

          2)用外部toolsjmap -dump:format=b,file=<filename.hprof> <pid>

          3)用外部toolsjconsole

          4)用外部工具:MAT

          5kill -3 <pid>

          6jstack -l <pid> > <dumpfile>

           

          一些排查方法:

          1,通過top consumers查找大對(duì)象,可以按照class、classloaderpackage進(jìn)行group by

          2,通過immediate dominator找到責(zé)任對(duì)象,對(duì)于快速定位一組對(duì)象的持有者非常有用,這個(gè)操作直接解決了“誰讓這些對(duì)象alive”的問題,而不是“誰有這些對(duì)象的引用”的問題,更直接高效;

          3,運(yùn)行classloader分析,這個(gè)重要性體現(xiàn)在亮點(diǎn):第一,應(yīng)用使用不同的classloader加載類,第二,不同classloader加載的類存儲(chǔ)在不同的永久代,這理論上也是可以被回收的。當(dāng)有一個(gè)類被不同的classloader加載時(shí),這時(shí)要根據(jù)各自loader下的instance數(shù)量判斷哪個(gè)loader更重要,從而要把另一個(gè)回收掉;

          4,分析線程,本身heap dump里包含了thread信息,可以通過MAT來查看threads overviewdetaildetail中有線程的堆內(nèi)存信息,也有線程棧,同時(shí)還包含了操作系統(tǒng)本地棧。假設(shè)不做heap dump,我們檢查到系統(tǒng)有問題,如何通過線程的角度來排查呢?首先top -H -p <pid>以線程的模式查看java應(yīng)用的運(yùn)行情況,找到占用cpu或者內(nèi)存大的線程,記錄線程id,然后printf %x <tid>轉(zhuǎn)為16進(jìn)制,再jstack -l <pid> > thread.logjava進(jìn)程的thread dump出來,從里面找到tid,分析是哪個(gè)線程占用了系統(tǒng)資源。

          5,分析java容器類,因?yàn)?/span>java的容器類是最常用來存儲(chǔ)對(duì)象的,所以理論上發(fā)生內(nèi)存泄露的風(fēng)險(xiǎn)也最高??梢詮膸讉€(gè)角度來看:1array填充率查詢(填充率fill ratio是數(shù)組中非空元素的比例),打印非原生類型數(shù)組的填充率頻率分布,從而排查系統(tǒng)中array的利用率;2)數(shù)組按照size分組查詢,打印一個(gè)按size分組的直方圖;3collection的填充率查詢,ArrayList/HashMap/Hashtable/Properties/Vector/WeakHashMap/ConcurrentHashMap$Segment;4collection按照size分組直方圖;5)查看一個(gè)list里的所有對(duì)象;6)查看hashmap里的所有對(duì)象;7)查看hashset里的對(duì)象;8)檢查map的碰撞率;9)檢查所有只有一個(gè)常量的array。

          6,分析Finalizer,1)查詢finalizer正在處理的對(duì)象;2)查詢finalizer準(zhǔn)備處理的對(duì)象;3)直接查看finalizer線程;4)查看finalizer線程的thread local對(duì)象。

          posted on 2015-08-17 19:08 changedi 閱讀(7473) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 济南市| 乌恰县| 新乡市| 班玛县| 四会市| 牟定县| 西平县| 楚雄市| 长兴县| 布拖县| 农安县| 佛冈县| 响水县| 万盛区| 黔西| 兴化市| 紫金县| 蒲城县| 台东县| 青海省| 萨迦县| 嵩明县| 安宁市| 江陵县| 庆元县| 长白| 纳雍县| 北安市| 南江县| 莲花县| 拉孜县| 正镶白旗| 九寨沟县| 定南县| 潼关县| 靖宇县| 东平县| 红安县| 洪雅县| 天峻县| 贺兰县|