在最近的工作中,通過JProfiler解決了一個記憶體洩漏的問題,現将檢測的步驟和一些分析記錄下來,已備今後遇到相似問題時可以作為參考。
運作環境:
Tomcat6,jdk6,JProfiler8
記憶體洩漏的現象:
1. 在伺服器中執行某些批量操作的時候,發現記憶體隻升不降;就算gc後,記憶體也不能被完全釋放;
2. 除非重新開機tomcat伺服器,記憶體永遠不會被釋放,反複執行這些操作,會導緻無可用記憶體,tomcat死掉;
使用JProfiler檢查記憶體洩漏的步驟:
1. 初始化檢驗環境:
切換到“Live Memory-->All Objects”标簽,可以看到目前tomcat中的對象情況,注意jprofiler其他版本可能位置不一樣.
在執行操作前,需要先F4,運作“Run GC”,使jvm進行記憶體回收清理無效的對象.為了便于比較記憶體的增長情況,可以點選右鍵--->"Mark Current",
來将目前記憶體使用情況作為參照;點選後會顯示“Difference”列,該列會列出對象數量的變化和變化比率
2.打開記憶體記錄:
點選“Start Recordings”按鈕,開始記錄。執行這步的主要目的是為下面“Heap Walker”設定一個監控區間;如果不記錄的話“Heap Walker”将分析jvm虛拟機的所有記憶體,即耗時又不能準确的發現記憶體洩漏的原因。
3. 執行操作,執行gc;
使用壓力工具通路被測應用,執行完之後再次F4進行GC----這樣是為了消除可以回收的對象。執行記憶體回收後,仍然存在于記憶體中的對象有可能是洩漏的對象。如下圖instance count中紅色的部門為不能回收的對象,difference列列出了增加的對象數量和增。以String為例,在該操作中增加了31751個對象增幅達到了14%,随後會在HeapWalker中觀察這些對象,分析哪些對象是洩漏的。一般引起洩漏的對象包括:String、char[]、HashMap、Concurrenthashmap等,這類對象需要重點關注下;
4. 關閉記憶體記錄:
點選“Stop Recordings”關閉記憶體記錄,告訴jProfiler把這段記錄作為分析對象;
5. 找到增加迅速的對象類型,打開HeapWalker:
在視圖中找到增長快速的對象類型,本例Concurrenthashmap的增長速度很快。在memory視圖中找到Concurrenthashmap---點右鍵----選擇“Show Selectiion In Heap Walker”,切換到HeapWarker 視圖;切換前會彈出選項頁面,注意一定要選擇“Select recorded objects”,這樣Heap Walker會在剛剛的那段記錄中進行分析;否則,會分析tomcat的所有記憶體對象,這樣既耗時又不準确;
6. 在HeapWalker中,找到洩漏的對象;
HeapWarker 會分析記憶體中的所有對象,包括對象的引用、建立、大小和數量;
HeapWarker視圖下方可以進行頁面切換:
通過切換到References頁簽,可以看到這個類的具體對象執行個體。
為了在這些記憶體對象中,找到洩漏的對象(應該被回收),可以在該對象上點選右鍵,選擇“Use Selected Instances”縮小對象範圍;
單擊OK按鈕
7. 通過引用分析該對象:
在References引用頁簽中,可以看到該對象的的引用關系,可以切換incoming/outcoming,顯示引用的類型:
incoming 表示顯示這個對象被誰引用;
outcoming 表示顯示這個對象引用的其他對象;
選擇“Show In Graph”将引用關系使用圖形方式展現;
選中該對象,點選“Show Paths To GC Root”,會找到引用的根節點;
在上圖中,我們可以發現,這個HashMap Segment對象最終的引用是在ConcurrentHashMap和ReentranLock對象中;
8. 通過建立分析該對象:
如果第7步還不能定位記憶體洩露的地方,我們可以嘗試使用Allocations頁簽,該頁簽顯示對象是如何建立出來的;
我們可以從建立方法開始檢查,檢查所有用到該對象的地方,直到找到洩漏位置;
持續學習、持續收獲才能帶來持續的滿足和快樂!