Java 程序中的內存泄漏呢?難道 Java 虛擬機( JVM )的垃圾收集器不應該管理未使用的內存嗎?是的,它會進行管理,但是垃圾收集的對象只能是不再被引用的對象。" name="description" />

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • JRockit JVM 中的內存泄漏檢測

    發表于:2007-04-29來源:作者:點擊數: 標簽:泄漏JRockitJVM檢測內存
    是什么導致了 java script:;" onClick="javascript:tagshow(event, 'Java');" target="_self"> Java 程序中的內存泄漏呢?難道 Java 虛擬機( JVM )的垃圾收集器不應該管理未使用的內存嗎?是的,它會進行管理,但是垃圾收集的對象只能是不再被引用的對象。
    是什么導致了 javascript:;" onClick="javascript:tagshow(event, 'Java');" target="_self">Java 程序中的內存泄漏呢?難道 Java 虛擬機( JVM )的垃圾收集器不應該管理未使用的內存嗎?是的,它會進行管理,但是垃圾收集的對象只能是不再被引用的對象。在 Java 中,典型的內存泄漏出現在以下情形中:不再需要某些對象,但是系統中仍有某個地方在引用它,這樣就不能對這些對象進行垃圾收集。

      在大型企業系統中, Java 代碼中的內存泄漏是常見而且難于解決的問題。這些泄漏問題通常是在部署之后發現的,難于在測試環境中得到重現。這是為什么呢?理由之一是,已部署的系統需要處理更大量的數據,而且有可能在執行數周之后才會發現 Java 堆在緩慢地增長。最終,這將導致系統內存耗盡。

      我們怎樣發現泄漏呢?市場上有很多可用的工具,但是其中大多數工具,要么是基于創建 Java 堆轉儲,并離線分析它,要么是基于 JVMPI ,而 JVMPI 會使應用程序的執行變得非常緩慢。在 BEA 的 JVM JRockit 1.4.2_05 版中,存在一個交互式內存泄漏檢測工具的技術預覽, JRockit Memory Leak Detector ,它支持在系統全速運行時使用。在本文中,我們將看一看這個試驗性的工具以及它可能發生的一些事情。我們還將試著展望一下該工具未來版本的功能,并復習一下內存泄漏的一些常見類型。

    Memory Leak Detector

      設計 Memory Leak Detector 的目的是將其用在生產環境中,而不會給系統增加很大的性能負擔。許多 JVM 內部的內存泄漏檢測功能直接構建在 JRockit 垃圾收集器內,以求獲得更高的性能。 JRockit 有一個管理控制臺,用于監控和管理一個或多個 JVM 。內存泄漏檢測工具當前被構建在 JRockit Management Console 中,但是在今年的 1.5 版之后的 JRockit JDK 中,可以把它當作一個單機工具來使用。其思路是,系統將幫助開發人員理解三件事情:是否有內存泄漏,如果有,泄漏的是什么,以及哪里出現泄漏。

      在我們進行詳細分析之前,我想要強調一點,這是一個低級的工具。例如,它不會告訴您泄漏出現在什么 EJB 中。數據從 JVM 被直接傳遞給 Memory Leak Detector ,而 JVM 沒有容器、 EJB 或者任何其他 J2EE 組件的概念。它只會處理類、數組和實例。只要您可以訪問源代碼,就應該不會引起任何問題。

    方法

      讓我們開始吧。第一步是確定系統中是否真的存在內存泄漏。為此,我們首先要在管理控制臺中創建一個連接,連接至我們感興趣的 JVM ,并確保系統正在運行且沒有過載。然后,轉到管理控制臺中的“ MemLeak Detector ”選項卡。這里,我們要在 JRockit 中啟用內存泄漏檢測系統,這將啟動內存增長的一個趨勢分析。 JVM 中每次進行垃圾收集時, JRockit 都會把趨勢數據發送給位于一個趨勢分析表中的 Memory Leak Detector (參見圖 1 )。

    實例研究

      趨勢分析顯示了堆(或者類,如果您更喜歡的話)上最常見的對象類型,以及用于這些類型的內存增長速率。它還給出了各個類型的當前內存使用和實例數量。趨勢分析的運行時間越長,趨勢就越可靠。各個類型是按照增長速率的順序排列的,而我們感興趣的正是增長速率最高的類型——這些就是泄漏的類型。在圖 1 的例子中,顯然有三種類型要為內存泄漏負責: DemoLeakDemoObject 、 HashtableEntry 、和 HashtableEntry[] 。我將使用這個例子作為一次實例研究,以便說明 JRockit Memory Leak Detector 的功能。

    哪里出現泄漏?

      現在,我們已經檢測到有內存泄漏,而且我們知道是何種類型的對象正在泄漏。在這個例子中,不難猜到是 混編 表項占用了 DemoObject ,因為這些類型的實例在數量上大體相等。此外, 混編 表項數組的增長也是合乎邏輯的,因為 混編 表項的數目正在不斷增長。

      要檢查混編表項是否正在使用 DemoObject ,我們可以要求查看何種類型指向 DemoObject 。為此,我們需要凍結趨勢分析的更新。那么,我們可以在表中選擇 DemoLeakDemoObject 行,然后點擊右鍵,就會出現一個帶有“ Show types pointing to this type ”選項的彈出式菜單。這將會顯示一個表,表中包含指向 DemoObject 的所有類型。在這種情況下,表只包含 HashtableEntry 類型。(啊哈,我們的猜測是正確的?。┈F在就可以對該表進行仔細分析,檢查是何種類型指向 HashtableEntry 。

      向后遍歷這個“指向( point-to )鏈”(參見圖 2 ),可以看到, HashtableEntry[] (數組)和 HashtableEntry 都指向了 HashtableEntry 類型。如果您認為后者比較古怪,記住如果 混編 表中出現了散列沖突,一個存儲段將會保存多個項,可以建立一個 混編 表項的鏈接表,并對其進行順序搜索。

      既然我們知道一個或多個混編表正在泄漏,我們就需要下降到實例的層次上。一種方式是找到增長的 HashtableEntry[] 實例,這很可能就是最大的一個數組。為此,我們可以右鍵單擊 HashtableEntry[] 類型,然后要求“ Show largest arrays of this type ”。這個動作將顯示一個表,其內容為最大的數組實例以及這些數組的內存大?。ㄒ宰止潪閱挝唬?。在我們的例子中,它看起來就像清單 1 一樣:

    java.util.HashtableEntry[]<10>	6 291 472
    java.util.HashtableEntry[]<3>	784
    java.util.HashtableEntry[]<4>	400
    java.util.HashtableEntry[]<5>	400
    java.util.HashtableEntry[]<6>	400
    java.util.HashtableEntry[]<7>	40
    java.util.HashtableEntry[]<8>	400
    java.util.HashtableEntry[]<9>	400
    java.util.HashtableEntry[]<11>	208
    java.util.HashtableEntry[]<12>	208
    

      括號中的數字 <n> 是惟一標識對象的對象 ID , JRockit 的內存泄漏檢測系統正好使用了它。這個列表告訴我們的是,帶有對象 ID<10> 的數組顯然很可疑,因為它消耗的內存比其他數組多。為了完全肯定,我們可以稍等一會,然后再次向系統請求最大的 HashtableEntry 數組的表。然后,我們可以檢查帶有 ID<10> 的數組消耗的內存是否仍然最多,結果發現它已經增長了數百個字節。

      讓我們扼要重述一下我們迄今為止的發現。系統正在泄漏內存,而且似乎有三種類型在泄漏—— DemoObject 、 HashtableEntry 和 HashtableEntry[] 。有一個混編表項的數組實例,這些混編表項還在增長,顯然,系統中只有一個混編表在泄漏。到底是哪一個呢?

      通過請求有關最大數組實例的指向信息,我們可以找出答案,具體方法是右鍵單擊最大的數組實例,然后選擇“ Show instances pointing to this array ”。這將會打開圖 3 中所示的窗口。它顯示了引用 HashtableEntry[]<10> 實例的實例或靜態字段(如果有的話)。正如我們看到的那樣,有一個實例指向數組:一個對象 ID 為 <20> 的混編表?,F在,我們已經找出泄漏的混編表,而且通過請求這個混編表的指向信息,我們可以繼續我們的搜索。結果在如圖 4 中的實例圖中以可視化的方式表示。有兩個 DemoThread 對象和許多 ObjectMonitor 對象引用了泄漏的混編表。我們可以不管 ObjectMonitor 對象,因為它們只是同步的一種結果,而與應用程序代碼無關。另一方面, DemoThread 非常值得關注,因為它是用戶代碼的一部分。

      為了幫助我們快速在引用混編表的 DemoThread 中找到正確的實例字段,我們可以在 DemoThread 對象之一上打開一個 Inspector 。 Inspector (參見圖 5 )顯示了實例的所有字段以及它們的值。列表中的第一個字段恰好是“ table ”,它指出 ID 為 <20> 的 Hashtable 是正在泄漏的混編表。 Memory Leak Detector 也能幫助我們做到這一點?,F在事情就變得很簡單了,只要讀取 DemoThread ( DemoLeak.java 中的一個內部類)的源代碼,然后弄清楚我們使用“ table ”字段的實際作用即可。

    限制

      我們進行研究的程序是一個簡單的例子,我只是出于明確性方面的考慮才選擇這個例子的?,F實中的生產系統要復雜的多,可能需要進行更加全面的研究。

      您還應該清楚, Memory Leak Detector 工具的這個技術預覽存在一些限制。一個限制是,它現在使用的是 JRockit Management Console 的基于 Java 的通信協議。這意味著 JRockit 需要創建新的 Java 對象才能發送信息給管理控制臺。這不是一個完美的解決方案,因為在內存出現泄漏的情況下,系統的內存很可能已經不夠用了。

      另一個限制是,當有大量信息需要發送時,我們就要冒著由于超時問題而丟失到管理控制臺連接的風險。這通常只在請求指向某種特定類型(理論上可以是數十萬種)的所有同類實例列表時,才成問題。在用戶界面中定位也著實不太讓人滿意。

      我們把這個工具視作對概念的驗證,而且我們急于獲得反饋,這樣我們便可以改進它未來的版本。即便它并不完美,我們仍然相信它對想找出內存泄漏的人來說十分有用。

    未來的工作

      那么,我們對于 Memory Leak Detector 的下一個版本有什么計劃呢?首先,它將成為一個單機工具,可以獨立于管理控制臺而運行。它將使用本機的通信協議,這應該可以根除當前工具中內存耗盡的風險。當然,更重要的事情是要改進用戶界面。以后的版本將會有對象和類型引用圖(類似于圖 2 和圖 4 中的內容)來可視化指向鏈,而且用戶進行定位的方式會比現今更加直觀。它還可以進行分配跟蹤,跟蹤給特定的對象類型分配的位置。當在代碼中進行精確定位時,使用這種功能將會十分方便。 JRockit 中的內存泄漏檢測系統仍然可以進行實時操作,同時將開銷降到最低,而且您還可以隨時啟用或禁用它。

    常見的泄漏

      內存泄漏很難檢測和解決,但值得慶幸的是許多程序員往往犯下同樣的錯誤。這聽起來完全不是什么好事,但同時也意味著大多數內存泄漏十分相似。因此,典型的內存泄漏的種類并不多,而且豐富的經驗和合適的工具,將有助于更容易地找出它們。下面,我將描述一些在服務器端常見的內存泄漏類型。

      一個十分常見的錯誤是,把對象放入混編表或混編圖中,但在不再需要它們時卻忘了刪除它們。我們的實例研究又是這樣一個例子:刪除混編表中的一些項時,出現了大小差 1 ( off-by-one )的錯誤。 DemoThread 中有錯誤的代碼如清單 2 所示。

    int total = 0;
    while (true)
    {
    for (int i = 0; i <= 60; i++)
    		table.put(new DemoObject(total + i), "foo");
    
     // Below is the faulty line: Should be <= and not just < 
    for (int i = 0; i < 60; i++)
    		table.remove(new DemoObject(total + i));
    
      total += 61;
    }
    
    

      這里給出的方法可以找出是哪個混編表實例正在泄漏,以及誰正在使用該實例,就像我們在實例研究中所做的那樣。希望這樣能夠給您足夠的信息,以便在您的代碼中找出正確的混編表。

      一種類似的情形是,何時存在復雜的數據結構,即某些混編表位于其他混編表的內部,以及一個或多個“內部”表在哪里泄漏。檢測方法和上面一樣,但是引用的信息會更加混亂,因為混編表項還可能指向混編表。

      另一種常見的內存泄漏出現在,把對象插入到一些其他類的集合中,但在使用之后沒有完全刪除的時候。使用 java.util.LinkedList 作為例子。在趨勢分析中,我們會看到 LinkedListEntry 類型正在增長。獲得該類型的引用者對我們沒有多大幫助,因為該類型指向它本身——它畢竟只是一個鏈接表?,F在正是時候來使用 Memory Leak Detector 中的“ Show instances of this type pointing to type ”選項了,這種情況下的 T 就是 LinkedListEntry 。這將顯示一個表,內容為指向其他項的項實例,匯聚了每個實例保持的活動數據的數量信息?;顒訑祿臄盗勘砻髁藢嵗褂玫膬却娲笮。俣ㄏ到y中只有該對象是活動的)?;顒訑祿疃嗟捻椡ǔJ切孤┝斜砩系牡谝豁?。找到這一項之后,我們可以獲得引用它的各個實例,從而了解系統中哪些地方引用了這個鏈接表。這樣做應該可以指導我們在代碼中找到正確的位置。

    結束語

      JRockit JVM 擁有一些用于實時內存泄漏檢測的獨特功能。在 Java 中,內存泄漏通常難于找出,但是借助合適的工具,它并非不可完成的任務。我已經說明了如何使用 JRockit Memory Leak Detector 檢測內存泄漏,找出泄漏的內容,然后仔細分析出是什么原因導致了代碼中出現的泄漏。


    原文轉自:http://www.kjueaiud.com

    評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
    ...

    熱門標簽

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>