成都心情

            BlogJava :: 首頁(yè) ::  :: 聯(lián)系 :: 聚合  :: 管理 ::
            98 隨筆 :: 2 文章 :: 501 評(píng)論 :: 1 Trackbacks

          公告

          Creative Commons License
          本作品采用知識(shí)共享署名-相同方式共享 2.5 中國(guó)大陸許可協(xié)議進(jìn)行許可。 Locations of visitors to this page(15)

          隨筆分類(91)

          隨筆檔案(99)

          文章分類(2)

          友情鏈接

          積分與排名

          • 積分 - 634730
          • 排名 - 74

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          前言的前言:本文是自20058月以來(lái),首次在一個(gè)月之內(nèi)發(fā)布三篇文章。謹(jǐn)以此文獻(xiàn)給這么多年始終不濟(jì)的我。所謂少不入川,而今已非年少。北漂快兩年了,何時(shí)能回到故鄉(xiāng),回去后又會(huì)怎樣,也許永遠(yuǎn)是個(gè)未知……

           

          前言

           

          在平時(shí)工作過(guò)程中,有時(shí)會(huì)遇到OutOfMemoryError,我們知道遇到Error一般表明程序存在著嚴(yán)重問(wèn)題,可能是災(zāi)難性的。所以找出是什么原因造成OutOfMemoryError非常重要?,F(xiàn)在向大家引薦Eclipse Memory Analyzer tool(MAT),來(lái)化解我們遇到的難題。如未說(shuō)明,本文均使用Java 5.0 on Windows XP SP3環(huán)境。

           

          為什么用MAT

           

          之前的觀點(diǎn),我認(rèn)為使用實(shí)時(shí)profiling/monitoring之類的工具,用一種非常實(shí)時(shí)的方式來(lái)分析哪里存在內(nèi)存泄漏是很正確的。年初使用了某profiler工具測(cè)試消息中間件中存在的內(nèi)存泄漏,發(fā)現(xiàn)在吞吐量很高的時(shí)候profiler工具自己也無(wú)法響應(yīng),這讓人很頭痛。后來(lái)了解到這樣的工具本身就要消耗性能,且在某些條件下還發(fā)現(xiàn)不了泄漏。所以,分析離線數(shù)據(jù)就非常重要了,MAT正是這樣一款工具。

           

          為何會(huì)內(nèi)存溢出

           

          我們知道JVM根據(jù)generation()來(lái)進(jìn)行GC,根據(jù)下圖所示,一共被分為young generation(年輕代)tenured generation(老年代)、permanent generation(永久代, perm gen),perm gen(或稱Non-Heap 非堆)是個(gè)異類,稍后會(huì)講到。注意,heap空間不包括perm gen


          絕大多數(shù)的對(duì)象都在young generation被分配,也在young generation被收回,當(dāng)young generation的空間被填滿,GC會(huì)進(jìn)行minor collection(次回收),這次回收不涉及到heap中的其他generation,minor collection根據(jù)weak generational hypothesis(弱年代假設(shè))來(lái)假設(shè)young generation中大量的對(duì)象都是垃圾需要回收,minor collection的過(guò)程會(huì)非??臁?/span>young generation中未被回收的對(duì)象被轉(zhuǎn)移到tenured generation,然而tenured generation也會(huì)被填滿,最終觸發(fā)major collection(主回收),這次回收針對(duì)整個(gè)heap,由于涉及到大量對(duì)象,所以比minor collection慢得多。

           

          JVM有三種垃圾回收器,分別是throughput collector,用來(lái)做并行young generation回收,由參數(shù)-XX:+UseParallelGC啟動(dòng);concurrent low pause collector,用來(lái)做tenured generation并發(fā)回收,由參數(shù)-XX:+UseConcMarkSweepGC啟動(dòng);incremental low pause collector,可以認(rèn)為是默認(rèn)的垃圾回收器。不建議直接使用某種垃圾回收器,最好讓JVM自己決斷,除非自己有足夠的把握。

           

          Heap中各generation空間是如何劃分的?通過(guò)JVM-Xmx=n參數(shù)可指定最大heap空間,而-Xms=n則是指定最小heap空間。在JVM初始化的時(shí)候,如果最小heap空間小于最大heap空間的話,如上圖所示JVM會(huì)把未用到的空間標(biāo)注為Virtual。除了這兩個(gè)參數(shù)還有-XX:MinHeapFreeRatio=n -XX:MaxHeapFreeRatio=n來(lái)分別控制最大、最小的剩余空間與活動(dòng)對(duì)象之比例。在32Solaris SPARC操作系統(tǒng)下,默認(rèn)值如下,在32windows xp下,默認(rèn)值也差不多。


          參數(shù)

          默認(rèn)值

          MinHeapFreeRatio

          40

          MaxHeapFreeRatio

          70

          -Xms

          3670k

          -Xmx

          64m


          由于tenured generationmajor collection較慢,所以tenured generation空間小于young generation的話,會(huì)造成頻繁的major collection,影響效率。Server JVM默認(rèn)的young generationtenured generation空間比例為1:2,也就是說(shuō)young generationedensurvivor空間之和是整個(gè)heap(當(dāng)然不包括perm gen)的三分之一,該比例可以通過(guò)-XX:NewRatio=n參數(shù)來(lái)控制,而Client JVM默認(rèn)的-XX:NewRatio8。至于調(diào)整young generation空間大小的NewSize=nMaxNewSize=n參數(shù)就不講了,請(qǐng)參考后面的資料。

           

          young generation中幸存的對(duì)象被轉(zhuǎn)移到tenured generation,但不幸的是concurrent collector線程在這里進(jìn)行major collection,而在回收任務(wù)結(jié)束前空間被耗盡了,這時(shí)將會(huì)發(fā)生Full Collections(Full GC),整個(gè)應(yīng)用程序都會(huì)停止下來(lái)直到回收完成。Full GC是高負(fù)載生產(chǎn)環(huán)境的噩夢(mèng)……

           

          現(xiàn)在來(lái)說(shuō)說(shuō)異類perm gen,它是JVM用來(lái)存儲(chǔ)無(wú)法在Java語(yǔ)言級(jí)描述的對(duì)象,這些對(duì)象分別是類和方法數(shù)據(jù)(與class loader有關(guān))以及interned strings(字符串駐留)。一般32OSperm gen默認(rèn)64m,可通過(guò)參數(shù)-XX:MaxPermSize=n指定,JVM Memory Structure一文說(shuō),對(duì)于這塊區(qū)域,沒(méi)有更詳細(xì)的文獻(xiàn)了,神秘。

           

          回到問(wèn)題“為何會(huì)內(nèi)存溢出?”。

          要回答這個(gè)問(wèn)題又要引出另外一個(gè)話題,既什么樣的對(duì)象GC才會(huì)回收?當(dāng)然是GC發(fā)現(xiàn)通過(guò)任何reference chain(引用鏈)無(wú)法訪問(wèn)某個(gè)對(duì)象的時(shí)候,該對(duì)象即被回收。名詞GC Roots正是分析這一過(guò)程的起點(diǎn),例如JVM自己確保了對(duì)象的可到達(dá)性(那么JVM就是GC Roots),所以GC Roots就是這樣在內(nèi)存中保持對(duì)象可到達(dá)性的,一旦不可到達(dá),即被回收。通常GC Roots是一個(gè)在current thread(當(dāng)前線程)call stack(調(diào)用棧)上的對(duì)象(例如方法參數(shù)和局部變量),或者是線程自身或者是system class loader(系統(tǒng)類加載器)加載的類以及native code(本地代碼)保留的活動(dòng)對(duì)象。所以GC Roots是分析對(duì)象為何還存活于內(nèi)存中的利器。知道了什么樣的對(duì)象GC才會(huì)回收后,再來(lái)學(xué)習(xí)下對(duì)象引用都包含哪些吧。

           

          從最強(qiáng)到最弱,不同的引用(可到達(dá)性)級(jí)別反映了對(duì)象的生命周期。

          l  Strong Ref(強(qiáng)引用):通常我們編寫(xiě)的代碼都是Strong Ref,于此對(duì)應(yīng)的是強(qiáng)可達(dá)性,只有去掉強(qiáng)可達(dá),對(duì)象才被回收。

          l  Soft Ref(軟引用):對(duì)應(yīng)軟可達(dá)性,只要有足夠的內(nèi)存,就一直保持對(duì)象,直到發(fā)現(xiàn)內(nèi)存吃緊且沒(méi)有Strong Ref時(shí)才回收對(duì)象。一般可用來(lái)實(shí)現(xiàn)緩存,通過(guò)java.lang.ref.SoftReference類實(shí)現(xiàn)。

          l  Weak Ref(弱引用):比Soft Ref更弱,當(dāng)發(fā)現(xiàn)不存在Strong Ref時(shí),立刻回收對(duì)象而不必等到內(nèi)存吃緊的時(shí)候。通過(guò)java.lang.ref.WeakReferencejava.util.WeakHashMap類實(shí)現(xiàn)。

          l  Phantom Ref(虛引用):根本不會(huì)在內(nèi)存中保持任何對(duì)象,你只能使用Phantom Ref本身。一般用于在進(jìn)入finalize()方法后進(jìn)行特殊的清理過(guò)程,通過(guò) java.lang.ref.PhantomReference實(shí)現(xiàn)。

           

          有了上面的種種我相信很容易就能把heapperm gen撐破了吧,是的利用Strong Ref,存儲(chǔ)大量數(shù)據(jù),直到heap撐破;利用interned strings(或者class loader加載大量的類)把perm gen撐破。

           

          關(guān)于shallow size、retained size

           

          Shallow size就是對(duì)象本身占用內(nèi)存的大小,不包含對(duì)其他對(duì)象的引用,也就是對(duì)象頭加成員變量(不是成員變量的值)的總和。在32位系統(tǒng)上,對(duì)象頭占用8字節(jié),int占用4字節(jié),不管成員變量(對(duì)象或數(shù)組)是否引用了其他對(duì)象(實(shí)例)或者賦值為null它始終占用4字節(jié)。故此,對(duì)于String對(duì)象實(shí)例來(lái)說(shuō),它有三個(gè)int成員(3*4=12字節(jié))、一個(gè)char[]成員(1*4=4字節(jié))以及一個(gè)對(duì)象頭(8字節(jié)),總共3*4 +1*4+8=24字節(jié)。根據(jù)這一原則,對(duì)String a=”rosen jiang”來(lái)說(shuō),實(shí)例ashallow size也是24字節(jié)(很多人對(duì)此有爭(zhēng)議,請(qǐng)看官甄別并留言給我)。

           

          Retained size是該對(duì)象自己的shallow size,加上從該對(duì)象能直接或間接訪問(wèn)到對(duì)象的shallow size之和。換句話說(shuō),retained size是該對(duì)象被GC之后所能回收到內(nèi)存的總和。為了更好的理解retained size,不妨看個(gè)例子。

           

          把內(nèi)存中的對(duì)象看成下圖中的節(jié)點(diǎn),并且對(duì)象和對(duì)象之間互相引用。這里有一個(gè)特殊的節(jié)點(diǎn)GC Roots,正解!這就是reference chain的起點(diǎn)。

          retained_objects.gifretained_objects_2.gif

          obj1入手,上圖中藍(lán)色節(jié)點(diǎn)代表僅僅只有通過(guò)obj1才能直接或間接訪問(wèn)的對(duì)象。因?yàn)榭梢酝ㄟ^(guò)GC Roots訪問(wèn),所以左圖的obj3不是藍(lán)色節(jié)點(diǎn);而在右圖卻是藍(lán)色,因?yàn)樗呀?jīng)被包含在retained集合內(nèi)。

          所以對(duì)于左圖,obj1retained sizeobj1obj2、obj4shallow size總和;右圖的retained sizeobj1、obj2obj3obj4shallow size總和。obj2retained size可以通過(guò)相同的方式計(jì)算。

           

          Heap Dump

           

          heap dump是特定時(shí)間點(diǎn),java進(jìn)程的內(nèi)存快照。有不同的格式來(lái)存儲(chǔ)這些數(shù)據(jù),總的來(lái)說(shuō)包含了快照被觸發(fā)時(shí)java對(duì)象和類在heap中的情況。由于快照只是一瞬間的事情,所以heap dump中無(wú)法包含一個(gè)對(duì)象在何時(shí)、何地(哪個(gè)方法中)被分配這樣的信息。

           

          在不同平臺(tái)和不同java版本有不同的方式獲取heap dump,而MAT需要的是HPROF格式的heap dump二進(jìn)制文件。想無(wú)需人工干預(yù)的話,要這樣配置JVM參數(shù):-XX:-HeapDumpOnOutOfMemoryError,當(dāng)錯(cuò)誤發(fā)生時(shí),會(huì)自動(dòng)生成heap dump,在生產(chǎn)環(huán)境中,只有用這種方式。如果你想自己控制什么時(shí)候生成heap dump,在Windows+JDK6環(huán)境中可利用JConsole工具,而在Linux或者Mac OS X環(huán)境下均可使用JDK5、6自帶的jmap工具。當(dāng)然,還可以配置JVM參數(shù):-XX:+HeapDumpOnCtrlBreak,也就是在控制臺(tái)使用Ctrl+Break鍵來(lái)生成heap dump。由于我是windows+JDK5,所以選擇了-XX:-HeapDumpOnOutOfMemoryError這種方式,更多配置請(qǐng)參考MAT Wiki。

           

          參考資料

           

          MAT Wiki

          Interned Strings

          Strong,Soft,Weak,Phantom Reference

          Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine

          Permanent Generation

          Understanding Weak References譯文

          Java HotSpot VM Options

          Shallow and retained sizes

          JVM Memory Structure

          GC roots


          請(qǐng)注意!引用、轉(zhuǎn)貼本文應(yīng)注明原作者:Rosen Jiang 以及出處: http://www.aygfsteel.com/rosen

          posted on 2010-05-21 20:59 Rosen 閱讀(75960) 評(píng)論(23)  編輯  收藏 所屬分類: Java 基礎(chǔ)

          評(píng)論

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2010-05-23 18:20 BeanSoft
          好好干! 前途是光明的! 當(dāng)然, 在京買(mǎi)房就別考慮了, 不現(xiàn)實(shí)!  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2010-05-24 09:51 Rosen
          @BeanSoft
          感謝BeanSoft兄關(guān)心,主要還是不會(huì)混,也錯(cuò)過(guò)一次次的機(jī)會(huì)。
          是啊,是要為后路考慮了。  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2010-05-25 15:02 liucr
          很精辟,感謝博主,你的前途一定會(huì)有。  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2010-12-15 09:15 Rosen
          @李文棟
          我是成都人,目前還是有6年工作時(shí)間了。  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2010-12-15 16:55 Rayleeya
          @Rosen
          多謝大蝦的博文。
            回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2011-01-26 17:31 耿恬
          不錯(cuò),受益匪淺。樓主這能力不要擔(dān)心前途  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2011-04-21 19:51 morning5607
          膜拜,同是成都人!  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2011-08-29 16:10 lijunwyf22
          很不錯(cuò),值得收藏。  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2011-10-19 18:14 fan
          嘻嘻  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2012-03-12 14:11 Locke
          首先謝謝樓主關(guān)于MAT的介紹。
          有一點(diǎn),-XX:+HeapDumpOnCtrlBreak這個(gè)參數(shù)在JDK6里面應(yīng)該是被取消了。我本地是JDK6_29版本。  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一)[未登錄](méi) 2012-06-27 11:23 IT民工
          最后也沒(méi)說(shuō)。是什么引發(fā)內(nèi)存泄露。。  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2012-07-18 17:40 ureygo
          說(shuō)的不錯(cuò),長(zhǎng)知識(shí)了  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一)[未登錄](méi) 2012-11-19 11:55 william
          有一個(gè)問(wèn)題想要請(qǐng)教一下:在圖2中假設(shè)

          obj1的shallow size = 1024b,
          obj2的shallow size = 1024b,
          obj3的shallow size = 1024b,
          obj4的shallow size = 1024b, 那么,按照您的算法:

          obj1的retain size = 4096b,
          obj2的retain size = 3072b,
          obj3的retain size = 1024b,
          obj3的retain size = 1024b,

          那實(shí)際的內(nèi)存占用大小是多少呢?

          1:4096+3027+1024+1024?
          2:1024+4*1 + 1024+4*2 + 1024 + 1024?

            回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2012-12-19 09:37 shuhucy
          -XX:-HeapDumpOnOutOfMemoryError

          這個(gè)配置是錯(cuò)的,正確的配置是:

          -XX:+HeapDumpOnOutOfMemoryError

          這樣才會(huì)打印堆轉(zhuǎn)儲(chǔ)文件

          同時(shí)建議配置:
          -XX:HeapDumpPath=D:\heapdump選項(xiàng),指定文件目錄,不然轉(zhuǎn)儲(chǔ)文件輸出到什么地方了,很多人找不到

            回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2013-06-16 22:09 woyaowenzi
          第一張圖沒(méi)了。  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2014-04-15 17:46 janeni_s
          很老厲害的博文 膜拜  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2014-09-17 19:01 chenlian
          我達(dá)州人,呵呵  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2014-09-17 19:01 chenlian
          QQ584314183  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2014-10-31 16:58 liyghting
          @woyaowenzi看了下源碼,引用的是sun公司的,估計(jì)地址換了,就沒(méi)有了。  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一)[未登錄](méi) 2014-12-24 16:31 dd
          @BeanSoft
            回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一)[未登錄](méi) 2015-04-20 15:02 yoyo
          贊一個(gè)  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2015-11-11 15:36 JDongfeng
          好文章,頂一個(gè)  回復(fù)  更多評(píng)論
            

          # re: 使用Memory Analyzer tool(MAT)分析內(nèi)存泄漏(一) 2016-06-14 11:17 袁良錠
          小瑕疵。
          圖片顯示不了。  回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 疏勒县| 河北区| 和田市| 嘉峪关市| 墨玉县| 万宁市| 新田县| 五指山市| 托克托县| 封开县| 卢龙县| 大名县| 上虞市| 东海县| 板桥市| 常州市| 富蕴县| 池州市| 老河口市| 湖北省| 拜城县| 古浪县| 西乡县| 恩施市| 会理县| 宝兴县| 沅陵县| 政和县| 长岭县| 佛坪县| 宜章县| 鄂尔多斯市| 黔南| 萨迦县| 阜新市| 大埔县| 边坝县| 盘山县| 丹阳市| 深圳市| 林周县|