国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
java內(nèi)存泄露 - 心靈花園 - JavaEye技術(shù)網(wǎng)站

java內(nèi)存泄露

http://tech.it168.com/j/n/2007-04-12/200704121248421_4.shtml

編者按:Java內(nèi)存泄漏是每個Java程序員都會遇到的問題,程序在本地運行一切正常,可是布署到遠(yuǎn)端就會出現(xiàn)內(nèi)存無限制的增長,最后系統(tǒng)癱瘓,那么如何最快最好的檢測程序的穩(wěn)定性,防止系統(tǒng)崩盤,作者用自已的親身經(jīng)歷與各位網(wǎng)友分享解決這些問題的辦法。

作為Internet最流行的編程語言之一,Java現(xiàn)正非常流行。我們的網(wǎng)絡(luò)應(yīng)用程序就主要采用Java語言開發(fā),大體上分為客戶端、服務(wù)器和數(shù)據(jù)庫三個層次。在進(jìn)入測試過程中,我們發(fā)現(xiàn)有一個程序模塊系統(tǒng)內(nèi)存和CPU資源消耗急劇增加,持續(xù)增長到出現(xiàn)java.lang.OutOfMemoryError為止。經(jīng)過分析Java內(nèi)存泄漏是破壞系統(tǒng)的主要因素。這里與大家分享我們在開發(fā)過程中遇到的Java內(nèi)存泄漏的檢測和處理解決過程
.

. Java是如何管理內(nèi)存


為了判斷Java中是否有內(nèi)存泄露,我們首先必須了解Java是如何管理內(nèi)存的。Java的內(nèi)存管理就是對象的分配和釋放問題。在Java中,內(nèi)存的分配是由程序完成的,而內(nèi)存的釋放是由垃圾收集器(Garbage Collection,GC)完成的,程序員不需要通過調(diào)用函數(shù)來釋放內(nèi)存,但它只能回收無用并且不再被其它對象引用的那些對象所占用的空間。


Java
的內(nèi)存垃圾回收機制是從程序的主要運行對象開始檢查引用鏈,當(dāng)遍歷一遍后發(fā)現(xiàn)沒有被引用的孤立對象就作為垃圾回收。GC為了能夠正確釋放對象,必須監(jiān)控每一個對象的運行狀態(tài),包括對象的申請、引用、被引用、賦值等,GC都需要進(jìn)行監(jiān)控。監(jiān)視對象狀態(tài)是為了更加準(zhǔn)確地、及時地釋放對象,而釋放對象的根本原則就是該對象不再被引用。


Java中,這些無用的對象都由GC負(fù)責(zé)回收,因此程序員不需要考慮這部分的內(nèi)存泄露。雖然,我們有幾個函數(shù)可以訪問GC,例如運行GC的函數(shù)System.gc(),但是根據(jù)Java語言規(guī)范定義,該函數(shù)不保證JVM的垃圾收集器一定會執(zhí)行。因為不同的JVM實現(xiàn)者可能使用不同的算法管理GC。通常GC的線程的優(yōu)先級別較低。JVM調(diào)用GC的策略也有很多種,有的是內(nèi)存使用到達(dá)一定程度時,GC才開始工作,也有定時執(zhí)行的,有的是平緩執(zhí)行GC,有的是中斷式執(zhí)行GC。但通常來說,我們不需要關(guān)心這些。

. 什么是Java中的內(nèi)存泄露

導(dǎo)致內(nèi)存泄漏主要的原因是,先前申請了內(nèi)存空間而忘記了釋放。如果程序中存在對無用對象的引用,那么這些對象就會駐留內(nèi)存,消耗內(nèi)存,因為無法讓垃圾回收器GC驗證這些對象是否不再需要。如果存在對象的引用,這個對象就被定義為"有效的活動",同時不會被釋放。要確定對象所占內(nèi)存將被回收,我們就要務(wù)必確認(rèn)該對象不再會被使用。典型的做法就是把對象數(shù)據(jù)成員設(shè)為null或者從集合中移除該對象。但當(dāng)局部變量不需要時,不需明顯的設(shè)為null,因為一個方法執(zhí)行完畢時,這些引用會自動被清理。

Java中,內(nèi)存泄漏就是存在一些被分配的對象,這些對象有下面兩個特點,首先,這些對象是有被引用的,即在有向樹形圖中,存在樹枝通路可以與其相連;其次,這些對象是無用的,即程序以后不會再使用這些對象。如果對象滿足這兩個條件,這些對象就可以判定為Java中的內(nèi)存泄漏,這些對象不會被GC所回收,然而它卻占用內(nèi)存。


這里引用一個??吹降睦?,在下面的代碼中,循環(huán)申請Object對象,并將所申請的對象放入一個Vector中,如果僅僅釋放對象本身,但因為Vector仍然引用該對象,所以這個對象對GC來說是不可回收的。因此,如果對象加入到Vector后,還必須從Vector中刪除,最簡單的方法就是將Vector對象設(shè)置為null

Vector v = new Vector(10);

for (int i = 1; i < 100; i++)

......{

 Object o = new Object();

 v.add(o);

 o = null;

}//此時,所有的Object對象都沒有被釋放,因為變量v引用這些對象。

實際上這些對象已經(jīng)是無用的,但還被引用,GC就無能為力了(事實上GC認(rèn)為它還有用),這一點是導(dǎo)致內(nèi)存泄漏最重要的原因。 再引用另一個例子來說明Java的內(nèi)存泄漏。假設(shè)有一個日志類Logger,其提供一個靜態(tài)的log(String msg),任何其它類都可以調(diào)用Logger.Log(message)來將message的內(nèi)容記錄到系統(tǒng)的日志文件中。

Logger
類有一個類型為HashMap的靜態(tài)變量temp,每次在執(zhí)行log(message)的時候,都首先將message的值寫入temp(以當(dāng)前線程+當(dāng)前時間為鍵),在退出之前再從temp中將以當(dāng)前線程和當(dāng)前時間為鍵的條目刪除。注意,這里當(dāng)前時間是不斷變化的,所以log在退出之前執(zhí)行刪除條目的操作并不能刪除執(zhí)行之初寫入的條目。這樣,任何一個作為參數(shù)傳給log的字符串最終由于被Logger的靜態(tài)變量temp引用,而無法得到回收,這種對象保持就是我們所說的Java內(nèi)存泄漏??偟膩碚f,內(nèi)存管理中的內(nèi)存泄漏產(chǎn)生的主要原因:保留下來卻永遠(yuǎn)不再使用的對象引用。

. 幾種典型的內(nèi)存泄漏

我們知道了在Java中確實會存在內(nèi)存泄漏,那么就讓我們看一看幾種典型的泄漏,并找出他們發(fā)生的原因和解決方法。

3.1
全局集合

在大型應(yīng)用程序中存在各種各樣的全局?jǐn)?shù)據(jù)倉庫是很普遍的,比如一個JNDI-tree或者一個session table。在這些情況下,必須注意管理儲存庫的大小。必須有某種機制從儲存庫中移除不再需要的數(shù)據(jù)。


通常有很多不同的解決形式,其中最常用的是一種周期運行的清除作業(yè)。這個作業(yè)會驗證倉庫中的數(shù)據(jù)然后清除一切不需要的數(shù)據(jù)。


另一種管理儲存庫的方法是使用反向鏈接(referrer)計數(shù)。然后集合負(fù)責(zé)統(tǒng)計集合中每個入口的反向鏈接的數(shù)目。這要求反向鏈接告訴集合何時會退出入口。當(dāng)反向鏈接數(shù)目為零時,該元素就可以從集合中移除了。

  

3.2
緩存

緩存一種用來快速查找已經(jīng)執(zhí)行過的操作結(jié)果的數(shù)據(jù)結(jié)構(gòu)。因此,如果一個操作執(zhí)行需要比較多的資源并會多次被使用,通常做法是把常用的輸入數(shù)據(jù)的操作結(jié)果進(jìn)行緩存,以便在下次調(diào)用該操作時使用緩存的數(shù)據(jù)。緩存通常都是以動態(tài)方式實現(xiàn)的,如果緩存設(shè)置不正確而大量使用緩存的話則會出現(xiàn)內(nèi)存溢出的后果,因此需要將所使用的內(nèi)存容量與檢索數(shù)據(jù)的速度加以平衡。


常用的解決途徑是使用java.lang.ref.SoftReference類堅持將對象放入緩存。這個方法可以保證當(dāng)虛擬機用完內(nèi)存或者需要更多堆的時候,可以釋放這些對象的引用。


3.3
類裝載器

Java
類裝載器的使用為內(nèi)存泄漏提供了許多可乘之機。一般來說類裝載器都具有復(fù)雜結(jié)構(gòu),因為類裝載器不僅僅是只與"常規(guī)"對象引用有關(guān),同時也和對象內(nèi)部的引用有關(guān)。比如數(shù)據(jù)變量,方法和各種類。這意味著只要存在對數(shù)據(jù)變量,方法,各種類和對象的類裝載器,那么類裝載器將駐留在JVM中。既然類裝載器可以同很多的類關(guān)聯(lián),同時也可以和靜態(tài)數(shù)據(jù)變量關(guān)聯(lián),那么相當(dāng)多的內(nèi)存就可能發(fā)生泄漏。


. 如何檢測和處理內(nèi)存泄漏
如何查找引起內(nèi)存泄漏的原因一般有兩個步驟:第一是安排有經(jīng)驗的編程人員對代碼進(jìn)行走查和分析,找出內(nèi)存泄漏發(fā)生的位置;第二是使用專門的內(nèi)存泄漏測試工具進(jìn)行測試。

第一個步驟在代碼走查的工作中,可以安排對系統(tǒng)業(yè)務(wù)和開發(fā)語言工具比較熟悉的開發(fā)人員對應(yīng)用的代碼進(jìn)行了交叉走查,盡量找出代碼中存在的數(shù)據(jù)庫連接聲明和結(jié)果集未關(guān)閉、代碼冗余等故障代碼。


第二個步驟就是檢測Java的內(nèi)存泄漏。在這里我們通常使用一些工具來檢查Java程序的內(nèi)存泄漏問題。市場上已有幾種專業(yè)檢查Java內(nèi)存泄漏的工具,它們的基本工作原理大同小異,都是通過監(jiān)測Java程序運行時,所有對象的申請、釋放等動作,將內(nèi)存管理的所有信息進(jìn)行統(tǒng)計、分析、可視化。開發(fā)人員將根據(jù)這些信息判斷程序是否有內(nèi)存泄漏問題。這些工具包括Optimizeit Profiler,JProbe Profiler,JinSight , Rational 公司的Purify等。


4.1
檢測內(nèi)存泄漏的存在

這里我們將簡單介紹我們在使用Optimizeit檢查的過程。通常在知道發(fā)生內(nèi)存泄漏之后,第一步是要弄清楚泄漏了什么數(shù)據(jù)和哪個類的對象引起了泄漏。

一般說來,一個正常的系統(tǒng)在其運行穩(wěn)定后其內(nèi)存的占用量是基本穩(wěn)定的,不應(yīng)該是無限制的增長的。同樣,對任何一個類的對象的使用個數(shù)也有一個相對穩(wěn)定的上限,不應(yīng)該是持續(xù)增長的。根據(jù)這樣的基本假設(shè),我們持續(xù)地觀察系統(tǒng)運行時使用的內(nèi)存的大小和各實例的個數(shù),如果內(nèi)存的大小持續(xù)地增長,則說明系統(tǒng)存在內(nèi)存泄漏,如果特定類的實例對象個數(shù)隨時間而增長(就是所謂的“增長率”),則說明這個類的實例可能存在泄漏情況。


另一方面通常發(fā)生內(nèi)存泄漏的第一個跡象是:在應(yīng)用程序中出現(xiàn)了OutOfMemoryError。在這種情況下,需要使用一些開銷較低的工具來監(jiān)控和查找內(nèi)存泄漏。雖然OutOfMemoryError也有可能應(yīng)用程序確實正在使用這么多的內(nèi)存;對于這種情況則可以增加JVM可用的堆的數(shù)量,或者對應(yīng)用程序進(jìn)行某種更改,使它使用較少的內(nèi)存。


但是,在許多情況下,OutOfMemoryError都是內(nèi)存泄漏的信號。一種查明方法是不間斷地監(jiān)控GC的活動,確定內(nèi)存使用量是否隨著時間增加。如果確實如此,就可能發(fā)生了內(nèi)存泄漏。
1

4.2處理內(nèi)存泄漏的方法
一旦知道確實發(fā)生了內(nèi)存泄漏,就需要更專業(yè)的工具來查明為什么會發(fā)生泄漏。JVM自己是不會告訴您的。這些專業(yè)工具從JVM獲得內(nèi)存系統(tǒng)信息的方法基本上有兩種:JVMTI和字節(jié)碼技術(shù)(byte code instrumentation)Java虛擬機工具接口(Java Virtual Machine Tools Interface,JVMTI)及其前身Java虛擬機監(jiān)視程序接口(Java Virtual Machine Profiling Interface,JVMPI)是外部工具與JVM通信并從JVM收集信息的標(biāo)準(zhǔn)化接口。字節(jié)碼技術(shù)是指使用探測器處理字節(jié)碼以獲得工具所需的信息的技術(shù)。

Optimizeit
Borland公司的產(chǎn)品,主要用于協(xié)助對軟件系統(tǒng)進(jìn)行代碼優(yōu)化和故障診斷,其中的Optimizeit Profiler主要用于內(nèi)存泄漏的分析。Profiler的堆視圖就是用來觀察系統(tǒng)運行使用的內(nèi)存大小和各個類的實例分配的個數(shù)的。


首先,Profiler會進(jìn)行趨勢分析,找出是哪個類的對象在泄漏。系統(tǒng)運行長時間后可以得到四個內(nèi)存快照。對這四個內(nèi)存快照進(jìn)行綜合分析,如果每一次快照的內(nèi)存使用都比上一次有增長,可以認(rèn)定系統(tǒng)存在內(nèi)存泄漏,找出在四個快照中實例個數(shù)都保持增長的類,這些類可以初步被認(rèn)定為存在泄漏。通過數(shù)據(jù)收集和初步分析,可以得出初步結(jié)論:系統(tǒng)是否存在內(nèi)存泄漏和哪些對象存在泄漏(被泄漏)。


接下來,看看有哪些其他的類與泄漏的類的對象相關(guān)聯(lián)。前面已經(jīng)談到Java中的內(nèi)存泄漏就是無用的對象保持,簡單地說就是因為編碼的錯誤導(dǎo)致了一條本來不應(yīng)該存在的引用鏈的存在(從而導(dǎo)致了被引用的對象無法釋放),因此內(nèi)存泄漏分析的任務(wù)就是找出這條多余的引用鏈,并找到其形成的原因。查看對象分配到哪里是很有用的。同時只知道它們?nèi)绾闻c其他對象相關(guān)聯(lián)(即哪些對象引用了它們)是不夠的,關(guān)于它們在何處創(chuàng)建的信息也很有用。


最后,進(jìn)一步研究單個對象,看看它們是如何互相關(guān)聯(lián)的。借助于Profiler工具,應(yīng)用程序中的代碼可以在分配時進(jìn)行動態(tài)添加,以創(chuàng)建堆棧跟蹤。也有可以對系統(tǒng)中所有對象分配進(jìn)行動態(tài)的堆棧跟蹤。這些堆棧跟蹤可以在工具中進(jìn)行累積和分析。對每個被泄漏的實例對象,必然存在一條從某個牽引對象出發(fā)到達(dá)該對象的引用鏈。處于堆??臻g的牽引對象在被從棧中彈出后就失去其牽引的能力,變?yōu)榉菭恳龑ο蟆R虼?,在長時間的運行后,被泄露的對象基本上都是被作為類的靜態(tài)變量的牽引對象牽引。

總而言之, Java雖然有自動回收管理內(nèi)存的功能,但內(nèi)存泄漏也是不容忽視,它往往是破壞系統(tǒng)穩(wěn)定性的重要因素。

1

. 什么是Java中的內(nèi)存泄露

導(dǎo)致內(nèi)存泄漏主要的原因是,先前申請了內(nèi)存空間而忘記了釋放。如果程序中存在對無用對象的引用,那么這些對象就會駐留內(nèi)存,消耗內(nèi)存,因為無法讓垃圾回收器GC驗證這些對象是否不再需要。如果存在對象的引用,這個對象就被定義為"有效的活動",同時不會被釋放。要確定對象所占內(nèi)存將被回收,我們就要務(wù)必確認(rèn)該對象不再會被使用。典型的做法就是把對象數(shù)據(jù)成員設(shè)為null或者從集合中移除該對象。但當(dāng)局部變量不需要時,不需明顯的設(shè)為null,因為一個方法執(zhí)行完畢時,這些引用會自動被清理。

Java中,內(nèi)存泄漏就是存在一些被分配的對象,這些對象有下面兩個特點,首先,這些對象是有被引用的,即在有向樹形圖中,存在樹枝通路可以與其相連;其次,這些對象是無用的,即程序以后不會再使用這些對象。如果對象滿足這兩個條件,這些對象就可以判定為Java中的內(nèi)存泄漏,這些對象不會被GC所回收,然而它卻占用內(nèi)存。


這里引用一個??吹降睦樱谙旅娴拇a中,循環(huán)申請Object對象,并將所申請的對象放入一個Vector中,如果僅僅釋放對象本身,但因為Vector仍然引用該對象,所以這個對象對GC來說是不可回收的。因此,如果對象加入到Vector后,還必須從Vector中刪除,最簡單的方法就是將Vector對象設(shè)置為null。

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)

http://www.CodeHighlighter.com/

-->Vector v = new Vector(10);

for (int i = 1; i < 100; i++)

......{

 Object o = new Object();

 v.add(o);

 o = null;

}//此時,所有的Object對象都沒有被釋放,因為變量v引用這些對象。

實際上這些對象已經(jīng)是無用的,但還被引用,GC就無能為力了(事實上GC認(rèn)為它還有用),這一點是導(dǎo)致內(nèi)存泄漏最重要的原因。 再引用另一個例子來說明Java的內(nèi)存泄漏。假設(shè)有一個日志類Logger,其提供一個靜態(tài)的log(String msg),任何其它類都可以調(diào)用Logger.Log(message)來將message的內(nèi)容記錄到系統(tǒng)的日志文件中。

Logger
類有一個類型為HashMap的靜態(tài)變量temp,每次在執(zhí)行log(message)的時候,都首先將message的值寫入temp(以當(dāng)前線程+當(dāng)前時間為鍵),在退出之前再從temp中將以當(dāng)前線程和當(dāng)前時間為鍵的條目刪除。注意,這里當(dāng)前時間是不斷變化的,所以log在退出之前執(zhí)行刪除條目的操作并不能刪除執(zhí)行之初寫入的條目。這樣,任何一個作為參數(shù)傳給log的字符串最終由于被Logger的靜態(tài)變量temp引用,而無法得到回收,這種對象保持就是我們所說的Java內(nèi)存泄漏??偟膩碚f,內(nèi)存管理中的內(nèi)存泄漏產(chǎn)生的主要原因:保留下來卻永遠(yuǎn)不再使用的對象引用。

. 幾種典型的內(nèi)存泄漏

我們知道了在Java中確實會存在內(nèi)存泄漏,那么就讓我們看一看幾種典型的泄漏,并找出他們發(fā)生的原因和解決方法。

3.1
全局集合

在大型應(yīng)用程序中存在各種各樣的全局?jǐn)?shù)據(jù)倉庫是很普遍的,比如一個JNDI-tree或者一個session table。在這些情況下,必須注意管理儲存庫的大小。必須有某種機制從儲存庫中移除不再需要的數(shù)據(jù)。


通常有很多不同的解決形式,其中最常用的是一種周期運行的清除作業(yè)。這個作業(yè)會驗證倉庫中的數(shù)據(jù)然后清除一切不需要的數(shù)據(jù)。


另一種管理儲存庫的方法是使用反向鏈接(referrer)計數(shù)。然后集合負(fù)責(zé)統(tǒng)計集合中每個入口的反向鏈接的數(shù)目。這要求反向鏈接告訴集合何時會退出入口。當(dāng)反向鏈接數(shù)目為零時,該元素就可以從集合中移除了。

  

3.2
緩存

緩存一種用來快速查找已經(jīng)執(zhí)行過的操作結(jié)果的數(shù)據(jù)結(jié)構(gòu)。因此,如果一個操作執(zhí)行需要比較多的資源并會多次被使用,通常做法是把常用的輸入數(shù)據(jù)的操作結(jié)果進(jìn)行緩存,以便在下次調(diào)用該操作時使用緩存的數(shù)據(jù)。緩存通常都是以動態(tài)方式實現(xiàn)的,如果緩存設(shè)置不正確而大量使用緩存的話則會出現(xiàn)內(nèi)存溢出的后果,因此需要將所使用的內(nèi)存容量與檢索數(shù)據(jù)的速度加以平衡。


常用的解決途徑是使用java.lang.ref.SoftReference類堅持將對象放入緩存。這個方法可以保證當(dāng)虛擬機用完內(nèi)存或者需要更多堆的時候,可以釋放這些對象的引用。


3.3
類裝載器

Java
類裝載器的使用為內(nèi)存泄漏提供了許多可乘之機。一般來說類裝載器都具有復(fù)雜結(jié)構(gòu),因為類裝載器不僅僅是只與"常規(guī)"對象引用有關(guān),同時也和對象內(nèi)部的引用有關(guān)。比如數(shù)據(jù)變量,方法和各種類。這意味著只要存在對數(shù)據(jù)變量,方法,各種類和對象的類裝載器,那么類裝載器將駐留在JVM中。既然類裝載器可以同很多的類關(guān)聯(lián),同時也可以和靜態(tài)數(shù)據(jù)變量關(guān)聯(lián),那么相當(dāng)多的內(nèi)存就可能發(fā)生泄漏。

. 如何檢測和處理內(nèi)存泄漏

如何查找引起內(nèi)存泄漏的原因一般有兩個步驟:第一是安排有經(jīng)驗的編程人員對代碼進(jìn)行走查和分析,找出內(nèi)存泄漏發(fā)生的位置;第二是使用專門的內(nèi)存泄漏測試工具進(jìn)行測試。


第一個步驟在代碼走查的工作中,可以安排對系統(tǒng)業(yè)務(wù)和開發(fā)語言工具比較熟悉的開發(fā)人員對應(yīng)用的代碼進(jìn)行了交叉走查,盡量找出代碼中存在的數(shù)據(jù)庫連接聲明和結(jié)果集未關(guān)閉、代碼冗余等故障代碼。


第二個步驟就是檢測Java的內(nèi)存泄漏。在這里我們通常使用一些工具來檢查Java程序的內(nèi)存泄漏問題。市場上已有幾種專業(yè)檢查Java內(nèi)存泄漏的工具,它們的基本工作原理大同小異,都是通過監(jiān)測Java程序運行時,所有對象的申請、釋放等動作,將內(nèi)存管理的所有信息進(jìn)行統(tǒng)計、分析、可視化。開發(fā)人員將根據(jù)這些信息判斷程序是否有內(nèi)存泄漏問題。這些工具包括Optimizeit Profiler,JProbe Profiler,JinSight , Rational 公司的Purify等。


4.1
檢測內(nèi)存泄漏的存在

這里我們將簡單介紹我們在使用Optimizeit檢查的過程。通常在知道發(fā)生內(nèi)存泄漏之后,第一步是要弄清楚泄漏了什么數(shù)據(jù)和哪個類的對象引起了泄漏。

一般說來,一個正常的系統(tǒng)在其運行穩(wěn)定后其內(nèi)存的占用量是基本穩(wěn)定的,不應(yīng)該是無限制的增長的。同樣,對任何一個類的對象的使用個數(shù)也有一個相對穩(wěn)定的上限,不應(yīng)該是持續(xù)增長的。根據(jù)這樣的基本假設(shè),我們持續(xù)地觀察系統(tǒng)運行時使用的內(nèi)存的大小和各實例的個數(shù),如果內(nèi)存的大小持續(xù)地增長,則說明系統(tǒng)存在內(nèi)存泄漏,如果特定類的實例對象個數(shù)隨時間而增長(就是所謂的增長率),則說明這個類的實例可能存在泄漏情況。


另一方面通常發(fā)生內(nèi)存泄漏的第一個跡象是:在應(yīng)用程序中出現(xiàn)了OutOfMemoryError。在這種情況下,需要使用一些開銷較低的工具來監(jiān)控和查找內(nèi)存泄漏。雖然OutOfMemoryError也有可能應(yīng)用程序確實正在使用這么多的內(nèi)存;對于這種情況則可以增加JVM可用的堆的數(shù)量,或者對應(yīng)用程序進(jìn)行某種更改,使它使用較少的內(nèi)存。


但是,在許多情況下,OutOfMemoryError都是內(nèi)存泄漏的信號。一種查明方法是不間斷地監(jiān)控GC的活動,確定內(nèi)存使用量是否隨著時間增加。如果確實如此,就可能發(fā)生了內(nèi)存泄漏。

14.2處理內(nèi)存泄漏的方法
一旦知道確實發(fā)生了內(nèi)存泄漏,就需要更專業(yè)的工具來查明為什么會發(fā)生泄漏。JVM自己是不會告訴您的。這些專業(yè)工具從JVM獲得內(nèi)存系統(tǒng)信息的方法基本上有兩種:JVMTI和字節(jié)碼技術(shù)(byte code instrumentation)。Java虛擬機工具接口(Java Virtual Machine Tools InterfaceJVMTI)及其前身Java虛擬機監(jiān)視程序接口(Java Virtual Machine Profiling Interface,JVMPI)是外部工具與JVM通信并從JVM收集信息的標(biāo)準(zhǔn)化接口。字節(jié)碼技術(shù)是指使用探測器處理字節(jié)碼以獲得工具所需的信息的技術(shù)。

Optimizeit
Borland公司的產(chǎn)品,主要用于協(xié)助對軟件系統(tǒng)進(jìn)行代碼優(yōu)化和故障診斷,其中的Optimizeit Profiler主要用于內(nèi)存泄漏的分析。Profiler的堆視圖就是用來觀察系統(tǒng)運行使用的內(nèi)存大小和各個類的實例分配的個數(shù)的。


首先,Profiler會進(jìn)行趨勢分析,找出是哪個類的對象在泄漏。系統(tǒng)運行長時間后可以得到四個內(nèi)存快照。對這四個內(nèi)存快照進(jìn)行綜合分析,如果每一次快照的內(nèi)存使用都比上一次有增長,可以認(rèn)定系統(tǒng)存在內(nèi)存泄漏,找出在四個快照中實例個數(shù)都保持增長的類,這些類可以初步被認(rèn)定為存在泄漏。通過數(shù)據(jù)收集和初步分析,可以得出初步結(jié)論:系統(tǒng)是否存在內(nèi)存泄漏和哪些對象存在泄漏(被泄漏)。


接下來,看看有哪些其他的類與泄漏的類的對象相關(guān)聯(lián)。前面已經(jīng)談到Java中的內(nèi)存泄漏就是無用的對象保持,簡單地說就是因為編碼的錯誤導(dǎo)致了一條本來不應(yīng)該存在的引用鏈的存在(從而導(dǎo)致了被引用的對象無法釋放),因此內(nèi)存泄漏分析的任務(wù)就是找出這條多余的引用鏈,并找到其形成的原因。查看對象分配到哪里是很有用的。同時只知道它們?nèi)绾闻c其他對象相關(guān)聯(lián)(即哪些對象引用了它們)是不夠的,關(guān)于它們在何處創(chuàng)建的信息也很有用。


最后,進(jìn)一步研究單個對象,看看它們是如何互相關(guān)聯(lián)的。借助于Profiler工具,應(yīng)用程序中的代碼可以在分配時進(jìn)行動態(tài)添加,以創(chuàng)建堆棧跟蹤。也有可以對系統(tǒng)中所有對象分配進(jìn)行動態(tài)的堆棧跟蹤。這些堆棧跟蹤可以在工具中進(jìn)行累積和分析。對每個被泄漏的實例對象,必然存在一條從某個牽引對象出發(fā)到達(dá)該對象的引用鏈。處于堆??臻g的牽引對象在被從棧中彈出后就失去其牽引的能力,變?yōu)榉菭恳龑ο?。因此,在長時間的運行后,被泄露的對象基本上都是被作為類的靜態(tài)變量的牽引對象牽引。

總而言之, Java雖然有自動回收管理內(nèi)存的功能,但內(nèi)存泄漏也是不容忽視,它往往是破壞系統(tǒng)穩(wěn)定性的重要因素。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Java系統(tǒng)中內(nèi)存泄漏測試方法的研究
Java基礎(chǔ)惡補 -- 內(nèi)存泄露、內(nèi)存溢出 -- lsl 很不錯的哦!
Java的內(nèi)存泄漏
java內(nèi)存泄露
納尼,Java 存在內(nèi)存泄漏嗎?
幾種典型的Java內(nèi)存泄漏
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服