1. 程式人生 > >JAVA記憶體洩露分析和解決方案及WINDOWS自帶檢視工具

JAVA記憶體洩露分析和解決方案及WINDOWS自帶檢視工具

Java記憶體洩漏是每個Java程式設計師都會遇到的問題,程式在本地執行一切正常,可是佈署到遠端就會出現記憶體無限制的增長,最後系統癱瘓,那麼如何最快最好的檢測程式的穩定性,防止系統崩盤,作者用自已的親身經歷與各位分享解決這些問題的辦法.

作為Internet最流行的程式語言之一,Java現正非常流行.我們的網路應用程式就主要採用Java語言開發,大體上分為客戶端、伺服器和資料庫三個層次.在進入測試過程中,我們發現有一個程式模組系統記憶體和CPU資源消耗急劇增加,持續增長到出現java.lang.OutOfMemoryError為止.經過分析Java記憶體洩漏是破壞系統的主要因素.這裡與大家分享我們在開發過程中遇到的Java記憶體洩漏的檢測和處理解決過程.

一. Java是如何管理記憶體

為了判斷Java中是否有記憶體洩露,我們首先必須瞭解Java是如何管理記憶體的.Java的記憶體管理就是物件的分配和釋放問題.在Java中,記憶體的分配是由程式完成的,而記憶體的釋放是由垃圾收集器(Garbage Collection,GC)完成的,程式設計師不需要通過呼叫函式來釋放記憶體,但它只能回收無用並且不再被其它物件引用的那些物件所佔用的空間.

Java的記憶體垃圾回收機制是從程式的主要執行物件開始檢查引用鏈,當遍歷一遍後發現沒有被引用的孤立物件就作為垃圾回收.GC為了能夠正確釋放物件,必須監控每一個物件的執行狀態,包括物件的申請、引用、被引用、賦值等,GC都需要進行監控.監視物件狀態是為了更加準確地、及時地釋放物件,而釋放物件的根本原則就是該物件不再被引用.

在Java中,這些無用的物件都由GC負責回收,因此程式設計師不需要考慮這部分的記憶體洩露.雖然,我們有幾個函式可以訪問GC,例如執行GC的函式System.gc(),但是根據Java語言規範定義,該函式不保證JVM的垃圾收集器一定會執行.因為不同的JVM實現者可能使用不同的演算法管理GC.通常GC的執行緒的優先級別較低.JVM呼叫GC的策略也有很多種,有的是記憶體使用到達一定程度時,GC才開始工作,也有定時執行的,有的是平緩執行GC,有的是中斷式執行GC.但通常來說,我們不需要關心這些.

二. 什麼是Java中的記憶體洩露

導致記憶體洩漏主要的原因是,先前申請了記憶體空間而忘記了釋放.如果程式中存在對無用物件的引用,那麼這些物件就會駐留記憶體,消耗記憶體,因為無法讓垃圾回收器GC驗證這些物件是否不再需要.如果存在物件的引用,這個物件就被定義為"有效的活動",同時不會被釋放.要確定物件所佔記憶體將被回收,我們就要務必確認該物件不再會被使用.典型的做法就是把物件資料成員設為null或者從集合中移除該物件.但當局部變數不需要時,不需明顯的設為null,因為一個方法執行完畢時,這些引用會自動被清理.

在Java中,記憶體洩漏就是存在一些被分配的物件,這些物件有下面兩個特點,首先,這些物件是有被引用的,即在有向樹形圖中,存在樹枝通路可以與其相連;其次,這些物件是無用的,即程式以後不會再使用這些物件.如果物件滿足這兩個條件,這些物件就可以判定為Java中的記憶體洩漏,這些物件不會被GC所回收,然而它卻佔用記憶體.

這裡引用一個常看到的例子,在下面的程式碼中,迴圈申請Object物件,並將所申請的物件放入一個Vector中,如果僅僅釋放物件本身,但因為Vector仍然引用該物件,所以這個物件對GC來說是不可回收的.因此,如果物件加入到Vector後,還必須從Vector中刪除,最簡單的方法就是將Vector物件設定為null.

實際上這些物件已經是無用的,但還被引用,GC就無能為力了(事實上GC認為它還有用),這一點是導致記憶體洩漏最重要的原因. 再引用另一個例子來說明Java的記憶體洩漏.假設有一個日誌類Logger,其提供一個靜態的log(String msg),任何其它類都可以呼叫Logger.Log(message)來將message的內容記錄到系統的日誌檔案中.

Logger類有一個型別為HashMap的靜態變數temp,每次在執行log(message)的時候,都首先將message的值寫入temp中(以當前執行緒+當前時間為鍵),在退出之前再從temp中將以當前執行緒和當前時間為鍵的條目刪除.注意,這裡當前時間是不斷變化的,所以log在退出之前執行刪除條目的操作並不能刪除執行之初寫入的條目.這樣,任何一個作為引數傳給log的字串最終由於被Logger的靜態變數temp引用,而無法得到回收,這種物件保持就是我們所說的Java記憶體洩漏. 總的來說,記憶體管理中的記憶體洩漏產生的主要原因:保留下來卻永遠不再使用的物件引用.

三. 幾種典型的記憶體洩漏

我們知道了在Java中確實會存在記憶體洩漏,那麼就讓我們看一看幾種典型的洩漏,並找出他們發生的原因和解決方法.

3.1 全域性集合

在大型應用程式中存在各種各樣的全域性資料倉庫是很普遍的,比如一個JNDI-tree或者一個session table.在這些情況下,必須注意管理儲存庫的大小.必須有某種機制從儲存庫中移除不再需要的資料.

通常有很多不同的解決形式,其中最常用的是一種週期執行的清除作業.這個作業會驗證倉庫中的資料然後清除一切不需要的資料.另一種管理儲存庫的方法是使用反向連結(referrer)計數.然後集合負責統計集合中每個入口的反向連結的數目.這要求反向連結告訴集合何時會退出入口.當反向連結數目為零時,該元素就可以從集合中移除了.

3.2 快取 
快取一種用來快速查詢已經執行過的操作結果的資料結構.因此,如果一個操作執行需要比較多的資源並會多次被使用,通常做法是把常用的輸入資料的操作結果進行快取,以便在下次呼叫該操作時使用快取的資料.快取通常都是以動態方式實現的,如果快取設定不正確而大量使用快取的話則會出現記憶體溢位的後果,因此需要將所使用的記憶體容量與檢索資料的速度加以平衡.

常用的解決途徑是使用java.lang.ref.SoftReference類堅持將物件放入快取.這個方法可以保證當虛擬機器用完記憶體或者需要更多堆的時候,可以釋放這些物件的引用.

3.3 類裝載器 *****
Java類裝載器的使用為記憶體洩漏提供了許多可乘之機.一般來說類裝載器都具有複雜結構,因為類裝載器不僅僅是隻與"常規"物件引用有關,同時也和物件內部的引用有關.比如資料變數,方法和各種類.這意味著只要存在對資料變數,方法,各種類和物件的類裝載器,那麼類裝載器將駐留在JVM中.既然類裝載器可以同很多的類關聯,同時也可以和靜態資料變數關聯,那麼相當多的記憶體就可能發生洩漏.

四. 如何檢測和處理記憶體洩漏

如何查詢引起記憶體洩漏的原因一般有兩個步驟:第一是安排有經驗的程式設計人員對程式碼進行走查和分析,找出記憶體洩漏發生的位置;第二是使用專門的記憶體洩漏測試工具進行測試.

第一個步驟:在程式碼走查的工作中,可以安排對系統業務和開發語言工具比較熟悉的開發人員對應用的程式碼進行了交叉走查,儘量找出程式碼中存在的資料庫連線宣告和結果集未關閉、程式碼冗餘等故障程式碼.

第二個步驟:就是檢測Java的記憶體洩漏.在這裡我們通常使用一些工具來檢查Java程式的記憶體洩漏問題.市場上已有幾種專業檢查Java記憶體洩漏的工具,它們的基本工作原理大同小異,都是通過監測Java程式執行時,所有物件的申請、釋放等動作,將記憶體管理的所有資訊進行統計、分析、視覺化.開發人員將根據這些資訊判斷程式是否有記憶體洩漏問題.這些工具包括Optimizeit Profiler,JProbe Profiler,JinSight , Rational 公司的Purify等.

4.1檢測記憶體洩漏的存在 
這裡我們將簡單介紹我們在使用Optimizeit檢查的過程.通常在知道發生記憶體洩漏之後,第一步是要弄清楚洩漏了什麼資料和哪個類的物件引起了洩漏.

一般說來,一個正常的系統在其執行穩定後其記憶體的佔用量是基本穩定的,不應該是無限制的增長的.同樣,對任何一個類的物件的使用個數也有一個相對穩定的上限,不應該是持續增長的.根據這樣的基本假設,我們持續地觀察系統執行時使用的記憶體的大小和各例項的個數,如果記憶體的大小持續地增長,則說明系統存在記憶體洩漏,如果特定類的例項物件個數隨時間而增長(就是所謂的“增長率”),則說明這個類的例項可能存在洩漏情況.

另一方面通常發生記憶體洩漏的第一個跡象是:在應用程式中出現了OutOfMemoryError.在這種情況下,需要使用一些開銷較低的工具來監控和查詢記憶體洩漏.雖然OutOfMemoryError也有可能應用程式確實正在使用這麼多的記憶體;對於這種情況則可以增加JVM可用的堆的數量,或者對應用程式進行某種更改,使它使用較少的記憶體.

但是,在許多情況下,OutOfMemoryError都是記憶體洩漏的訊號.一種查明方法是不間斷地監控GC的活動,確定記憶體使用量是否隨著時間增加.如果確實如此,就可能發生了記憶體洩漏.

4.2處理記憶體洩漏的方法 
一旦知道確實發生了記憶體洩漏,就需要更專業的工具來查明為什麼會發生洩漏.JVM自己是不會告訴您的.這些專業工具從JVM獲得記憶體系統資訊的方法基本上有兩種:JVMTI和位元組碼技術(byte code instrumentation).Java虛擬機器工具介面(Java Virtual Machine Tools Interface,JVMTI)及其前身Java虛擬機器監視程式介面(Java Virtual Machine Profiling Interface,JVMPI)是外部工具與JVM通訊並從JVM收集資訊的標準化介面.位元組碼技術是指使用探測器處理位元組碼以獲得工具所需的資訊的技術.

Optimizeit是Borland公司的產品,主要用於協助對軟體系統進行程式碼優化和故障診斷,其中的Optimizeit Profiler主要用於記憶體洩漏的分析.Profiler的堆檢視就是用來觀察系統執行使用的記憶體大小和各個類的例項分配的個數的.

首先,Profiler會進行趨勢分析,找出是哪個類的物件在洩漏.系統執行長時間後可以得到四個記憶體快照.對這四個記憶體快照進行綜合分析,如果每一次快照的記憶體使用都比上一次有增長,可以認定系統存在記憶體洩漏,找出在四個快照中例項個數都保持增長的類,這些類可以初步被認定為存在洩漏.通過資料收集和初步分析,可以得出初步結論:系統是否存在記憶體洩漏和哪些物件存在洩漏(被洩漏).

接下來,看看有哪些其他的類與洩漏的類的物件相關聯.前面已經談到Java中的記憶體洩漏就是無用的物件保持,簡單地說就是因為編碼的錯誤導致了一條本來不應該存在的引用鏈的存在(從而導致了被引用的物件無法釋放),因此記憶體洩漏分析的任務就是找出這條多餘的引用鏈,並找到其形成的原因.檢視物件分配到哪裡是很有用的.同時只知道它們如何與其他物件相關聯(即哪些物件引用了它們)是不夠的,關於它們在何處建立的資訊也很有用.

最後,進一步研究單個物件,看看它們是如何互相關聯的.藉助於Profiler工具,應用程式中的程式碼可以在分配時進行動態新增,以建立堆疊跟蹤.也有可以對系統中所有物件分配進行動態的堆疊跟蹤.這些堆疊跟蹤可以在工具中進行累積和分析.對每個被洩漏的例項物件,必然存在一條從某個牽引物件出發到達該物件的引用鏈.處於堆疊空間的牽引物件在被從棧中彈出後就失去其牽引的能力,變為非牽引物件.因此,在長時間的執行後,被洩露的物件基本上都是被作為類的靜態變數的牽引物件牽引.

總而言之, Java雖然有自動回收管理記憶體的功能,但記憶體洩漏也是不容忽視,它往往是破壞系統穩定性的重要因素.

Windows自帶的Java記憶體檢視命令

jinfo:可以輸出並修改執行時的java 程序的opts。 
jps:與unix上的ps類似,用來顯示本地的java程序,可以檢視本地執行著幾個java程式,並顯示他們的程序號。 
jstat:一個極強的監視VM記憶體工具。可以用來監視VM記憶體內的各種堆和非堆的大小及其記憶體使用量。 
jmap:打印出某個java程序(使用pid)記憶體內的所有'物件'的情況(如:產生那些物件,及其數量)。 
jconsole:一個java GUI監視工具,可以以圖表化的形式顯示各種資料。並可通過遠端連線監視遠端的伺服器VM。 

詳細:在使用這些工具前,先用JPS命令獲取當前的每個JVM程序號,然後選擇要檢視的JVM。 
jstat工具特別強大,有眾多的可選項,詳細檢視堆內各個部分的使用量,以及載入類的數量。使用時,需加上檢視程序的程序id,和所選引數。以下詳細介紹各個引數的意義。 
jstat -class pid:顯示載入class的數量,及所佔空間等資訊。 
jstat -compiler pid:顯示VM實時編譯的數量等資訊。 
jstat -gc pid:可以顯示gc的資訊,檢視gc的次數,及時間。其中最後五項,分別是young gc的次數,young gc的時間,full gc的次數,full gc的時間,gc的總時間。 
jstat -gccapacity:可以顯示,VM記憶體中三代(young,old,perm)物件的使用和佔用大小,如:PGCMN顯示的是最小perm的記憶體使用量,PGCMX顯示的是perm的記憶體最大使用量,PGC是當前新生成的perm記憶體佔用量,PC是但前perm記憶體佔用量。其他的可以根據這個類推, OC是old內純的佔用量。 
jstat -gcnew pid:new物件的資訊。 
jstat -gcnewcapacity pid:new物件的資訊及其佔用量。 
jstat -gcold pid:old物件的資訊。 
jstat -gcoldcapacity pid:old物件的資訊及其佔用量。 
jstat -gcpermcapacity pid: perm物件的資訊及其佔用量。 
jstat -util pid:統計gc資訊統計。 
jstat -printcompilation pid:當前VM執行的資訊。 
除了以上一個引數外,還可以同時加上 兩個數字,如:jstat -printcompilation 3024 250 6是每250毫秒列印一次,一共列印6次,還可以加上-h3每三行顯示一下標題。 

jmap是一個可以輸出所有記憶體中物件的工具,甚至可以將VM 中的heap,以二進位制輸出成文字。 
命令:jmap -dump:format=b,file=heap.bin <pid> 
file:儲存路徑及檔名 
pid:程序編號 
•jmap -histo:live  pid| less :堆中活動的物件以及大小 
•jmap -heap pid : 檢視堆的使用狀況資訊 


jinfo:的用處比較簡單,就是能輸出並修改執行時的java程序的執行引數。用法是jinfo -opt pid 如:檢視2788的MaxPerm大小可以用 jinfo -flag MaxPermSize 2788。 

jconsole是一個用java寫的GUI程式,用來監控VM,並可監控遠端的VM,非常易用,而且功能非常強。使用方法:命令列裡打 jconsole,選則程序就可以了。 



JConsole中關於記憶體分割槽的說明。 

Eden Space (heap): 記憶體最初從這個執行緒池分配給大部分物件。 
Survivor Space (heap):用於儲存在eden space記憶體池中經過垃圾回收後沒有被回收的物件。 
Tenured Generation (heap):用於保持已經在 survivor space記憶體池中存在了一段時間的物件。 
Permanent Generation (non-heap): 儲存虛擬機器自己的靜態(refective)資料,例如類(class)和方法(method)物件。Java虛擬機器共享這些類資料。這個區域被分割為只讀的和只寫的, 
Code Cache (non-heap):HotSpot Java虛擬機器包括一個用於編譯和儲存原生代碼(native code)的記憶體,叫做“程式碼快取區”(code cache) 

•jstack ( 檢視jvm執行緒執行狀態,是否有死鎖現象等等資訊) : jstack pid : thread dump 
•jstat -gcutil  pid  1000 100  : 1000ms統計一次gc情況統計100次; 

另外推薦一款檢視jmap dump 的記憶體物件工具 MemoryAnalyzer 
網址:http://www.eclipse.org/mat/,可以檢視dump時物件數量,記憶體佔用,執行緒情況等。

以及使用Memory Analyzer tool分析記憶體洩露

http://www.blogjava.net/rosen/archive/2010/05/21/321575.html

https://www.cnblogs.com/yinliang/p/6431677.html

相關推薦

JAVA記憶體洩露分析解決方案WINDOWS檢視工具

Java記憶體洩漏是每個Java程式設計師都會遇到的問題,程式在本地執行一切正常,可是佈署到遠端就會出現記憶體無限制的增長,最後系統癱瘓,那麼如何最快最好的檢測程式的穩定性,防止系統崩盤,作者用自已的親身經歷與各位分享解決這些問題的辦法.作為Internet最流行的程式語言之一,Java現正非常流行.我們的網

Handler記憶體洩露分析解決方案

一、記憶體洩露分析 內部類會有一個指向外部類的引用。 垃圾回收機制中約定,當記憶體中的一個物件的引用計數為0時,將會被回收。 Handler 作為 Android 上的非同步訊息處理機制(好吧,我大多用來進行 worker thread 與 UI

京東某系統雙十一記憶體飆升分析解決方案

一、問題現象 系統在雙十一期間出現頻繁記憶體飆升現象,記憶體在幾天內直接飆升到報警閾值。在堆記憶體空間由3g調整到4g後,依然出現堆記憶體超過閾值問題,同時在重啟若干次記憶體依然會飆升堆積。同時也發現了jvm一直不出現full gc,young gc稍微有點顫

應用記憶體洩露起因與解決方案分析

java gc機制 java記憶體管理與c/c++不同,java使用garbage collection機制,由虛擬機器管理記憶體。在大部分虛擬機器(包括android的ART)中,都採用了“可達性”分析演算法來進行記憶體管理。 原理是:選取某幾個root節

記憶體溢位之PermGen OOM深入分析解決方案

閱讀原文 *現在,網上關於討論PermGen OOM的資料很多,但是深入分析PermGen區域記憶體溢位原因的資料很少。本篇文章嘗試全面分析一下PermGen OOM的原因,其中涉及到了Java虛擬機器執行時資料區、型別裝載、型別解除安裝等,測試程式碼涉及到了JMX協議。

資料庫連線池記憶體洩漏問題的分析解決方案

## 一、問題描述 上週五晚上主營出現部分裝置掉線,經過檢視日誌發現是由於快取系統出現長時間gc導致的。這裡的gc日誌的特點是: - 1.gc時間都在2s以上,部分節點甚至出現12s超長時間gc。 - 2.同一個節點距離上次gc時間間隔為普遍為13~15天。 ![](https://user-gold-cd

關於PHP的 PHP-FPM程序CPU 100%的一些原因分析解決方案

之前碰到過php-fpmCPU高達80%-90%,特此記錄下 1. 檢視是否是硬體問題   方式:top  命令   主要檢視:load average(平均負載),這是一個4核8G記憶體的伺服器   1分鐘平均負載:2.32;   5分鐘平均負載:2.18;   15分鐘平均負載:3.95;   loa

遊戲紋理抖動的成因分析解決方案

一、問題 在相機隨主角移動的過程中,發現地圖的細節部分不停的閃爍。我們用草地圖片做背景,圖片濾波模式設定為point,然後讓攝像頭沿著X軸緩慢移動,可以看到在移動過程中,圖片的某些部分抖動得很厲害,這種程度的抖動對於一般遊戲來說都是不可以接受的,因此網上也有些解決方

android 6 0許可權全面詳細分析解決方案

原文: http://www.2cto.com/kf/201512/455888.html http://blog.csdn.net/yangqingqo/article/details/48371123 http://inthecheesefactory.com/blog/things-y

Connection reset原因分析解決方案

在使用HttpClient呼叫後臺resetful服務時,“Connection reset”是一個比較常見的問題,有同學跟我私信說被這個問題困擾很久了,今天就來分析下,希望能幫到大家。例如我們線上的閘道器日誌就會拋該錯誤: 從日誌中可以看到是Socket套接字

android 6.0許可權全面詳細分析解決方案

原文: http://www.2cto.com/kf/201512/455888.html http://blog.csdn.net/yangqingqo/article/details/48371123 http://inthecheesefactory.com/blog

Android PermissionChecker 許可權全面詳細分析解決方案

原文:http://www.2cto.com/kf/201512/455888.htmlhttp://blog.csdn.net/yangqingqo/article/details/48371123http://inthecheesefactory.com/blog/thi

[Email]各種問題的分析解決方案

    3.1) Possible solution 1:You have a firewall. The firewall may be causing this error. Configure your firewall software to grant access to the Internet

簡訊驗證碼接收不到原因分析解決方案分析

簡訊驗證碼是通過傳送驗證碼到手機的一種有效的驗證碼系統。無論是大型網站尤其是購物網站,都提供有手機簡訊驗證碼功能,可以比較準確和安全地保證購物的安全性,驗證使用者的正確性。創藍253簡訊服務平臺上總結了簡訊驗證碼接收不到的幾個原因:(1)簡訊閘道器擁堵或出現異常在一些節假日或

Java記憶體溢位的詳細解決方案

一、記憶體溢位型別1、java.lang.OutOfMemoryError: PermGen spaceJVM管理兩種型別的記憶體,堆和非堆。堆是給開發人員用的上面說的就是,是在JVM啟動時建立;非堆是留給JVM自己用的,用來存放類的資訊的。它和堆不同,執行期內GC不會釋放空

Android應用記憶體洩露分析以及優化方案

文章轉載http://blog.csdn.net/Soiol/article/details/52486871        本篇部落格是介紹Android記憶體優化方面的知識,在讀本篇部落格之前需要你熟練掌握Java 基礎知識(例如,靜態變數的生命週期,匿名內部類的使用,匿名物件等),並且具有一定的Andr

安卓記憶體洩露成因解決辦法

記憶體管理的目的就是讓我們在開發中怎麼有效的避免我們的應用出現記憶體洩漏的問題。記憶體洩漏大家都不陌生了,簡單粗俗的講,就是該被釋放的物件沒有釋放,一直被某個或某些例項所持有卻不再被使用導致 GC 不能回收。最近自己閱讀了大量相關的文件資料,打算做個 總結 沉澱下來跟大家一起

C#/.NET基於Topshelf建立Windows服務的守護程式作為服務啟動的客戶端桌面程式不顯示UI介面的問題分析解決方案

本文首發於:碼友網--一個專注.NET/.NET Core開發的程式設計愛好者社群。 文章目錄 C#/.NET基於Topshelf建立Windows服務的系列文章目錄: C#/.NET基於Topshelf建立Windows服務程式及服務的安裝和解除安裝 (1) 在C#/.NET應用程式開發中建立一個基於To

QT如何定義滑鼠樣式如何用windows畫圖工具進行滑鼠樣式的設計

眾所周知,QT系統自身擁有眾多的滑鼠樣式,這些滑鼠樣式均比較常見,顏色、樣式為系統所固有,無法更改。哪麼能不能自定義滑鼠的形狀、顏色等特點呢?答案是可以的。自定義的滑鼠樣式可以隨意指定,顏色更是多種多樣,極具個性化。 QT自帶的滑鼠樣式(在此不做過多介紹): 可以直接拿來用,具體用法如下:

分析解決JAVA 記憶體洩露的實戰例子

這幾天,一直在為Java的“記憶體洩露”問題糾結。Java應用程式佔用的記憶體在不斷的、有規律的上漲,最終超過了監控閾值。福爾摩 斯不得不出手了! 分析記憶體洩露的一般步驟     如果發現Java應用程式佔用的記憶體出現了洩露的跡象,那麼我們一般採用下面的步驟分