在最近的工做中,經過JProfiler解決了一個內存泄漏的問題,現將檢測的步驟和一些分析記錄下來,已備從此遇到類似問題時能夠做爲參考。tomcat
運行環境:
Tomcat6,jdk6,JProfiler8
內存泄漏的現象:
1. 在服務器中執行某些批量操做的時候,發現內存只升不降;就算gc後,內存也不能被徹底釋放;
2. 除非重啓tomcat服務器,內存永遠不會被釋放,反覆執行這些操做,會致使無可用內存,tomcat死掉;
使用JProfiler檢查內存泄漏的步驟:
1. 初始化檢驗環境:
切換到「Live Memory-->All Objects」標籤,能夠看到當前tomcat中的對象狀況,注意jprofiler其餘版本可能位置不同.服務器
在執行操做前,須要先F4,運行「Run GC」,使jvm進行內存回收清理無效的對象.爲了便於比較內存的增加狀況,能夠點擊右鍵--->"Mark Current",jvm
來將當前內存使用狀況做爲參照;點擊後會顯示「Difference」列,該列會列出對象數量的變化和變化比率工具
2.打開內存記錄:
點擊「Start Recordings」按鈕,開始記錄。執行這步的主要目的是爲下面「Heap Walker」設置一個監控區間;若是不記錄的話「Heap Walker」將分析jvm虛擬機的全部內存,即耗時又不能準確的發現內存泄漏的緣由。spa
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的全部內存對象,這樣既耗時又不許確;3d
6. 在HeapWalker中,找到泄漏的對象;
HeapWarker 會分析內存中的全部對象,包括對象的引用、建立、大小和數量;
HeapWarker視圖下方能夠進行頁面切換:
經過切換到References頁籤,能夠看到這個類的具體對象實例。對象
爲了在這些內存對象中,找到泄漏的對象(應該被回收),能夠在該對象上點擊右鍵,選擇「Use Selected Instances」縮小對象範圍;blog
單擊OK按鈕內存
7. 經過引用分析該對象:
在References引用頁籤中,能夠看到該對象的的引用關係,能夠切換incoming/outcoming,顯示引用的類型:
incoming 表示顯示這個對象被誰引用;虛擬機
outcoming 表示顯示這個對象引用的其餘對象;
選擇「Show In Graph」將引用關係使用圖形方式展示;
選中該對象,點擊「Show Paths To GC Root」,會找到引用的根節點;
在上圖中,咱們能夠發現,這個HashMap Segment對象最終的引用是在ConcurrentHashMap和ReentranLock對象中;
8. 經過建立分析該對象:
若是第7步還不能定位內存泄露的地方,咱們能夠嘗試使用Allocations頁籤,該頁籤顯示對象是如何建立出來的;
咱們能夠從建立方法開始檢查,檢查全部用到該對象的地方,直到找到泄漏位置;