2012-03-29 41 views
2

我正在學習和測試一些圖庫,並遇到一個奇怪的問題(但這不是特定於圖形,我非常確定這是一般的Java相關)。我得到:'java.lang.OutOfMemoryError:超出GC開銷限制'。 I understand this error means that garbage collecting spending most of the cpu time and not returning any memory但我不確定如何解決此問題。爲什麼我得到'java.lang.OutOfMemoryError:超出GC開銷限制',如果我有大量的可用內存給JVM?

基本上我(爲了學習目的)想看看在內存中創建大量圖節點需要多長時間。我的系統運行的是美分,我有7個ram的演出,但是程序從未超過25%(我可以通過'top'看到),即使我通過運行'java -jar jungtester .jar -Xmx7g -XX:+ UseConcMarkSweepGC -XX:-UseGCOverheadLimit'(jungtester.jar是我的程序)。看起來它沒有使用所有可用的內存,並在大約350萬個節點之後死亡,這很奇怪,因爲它只是一個for循環,所以我認爲它只是在添加節點時突然停止,直到內存已滿。

我對JVM的內部工作原理相當陌生,所以關於如何克服這個問題的任何建議都會很棒。

如果有幫助,下面的代碼:

import edu.uci.ics.jung.graph.DirectedGraph; 
import edu.uci.ics.jung.graph.DirectedSparseGraph; 

public class main { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     // TODO Auto-generated method stub 
     System.out.println("starting..."); 
     long startTime = System.currentTimeMillis(); 
     DirectedGraph<Integer,Integer> graph = new DirectedSparseGraph<Integer, Integer>();; 
     graph.addVertex(1); 
     graph.addVertex(2); 
     graph.addEdge(1, 1,2); 

     for (int i = 0; i < 1000000; i++) { 
      graph.addVertex(i); 
      //System.out.println(i + " means we are " + (float) i/1000000 + "% done."); 
     } 
     long endTime1 = System.currentTimeMillis(); 
     System.out.println("done adding 1000000 in " + (endTime1 - startTime)); 


     for (int i = 1000001; i < 10000000; i++) { 
      graph.addVertex(i); 
      graph.addEdge(i, i, i-1000000); 
      System.out.println(i + " means we are " + (float) i/1000000000 + "% done."); 
     } 

     long endTime = System.currentTimeMillis(); 

     System.out.println("It took " + (endTime - startTime)); 
    } 

} 

更新:我得到它的工作,我不知道爲什麼,但爲了事宜。我把上面的命令放入-jar後沒有任何操作,但是當我將-jar添加到最後時,它似乎可以正常工作。

+1

使用jconsole(如果您有X可用)在應用程序運行時監視JVM,它會顯示正在發生的事情。 Top並沒有像Jconsole那樣顯示JVM實時統計數據。 – robertvoliva 2012-03-29 14:53:44

+1

你是在32位還是64位機器上運行它? 32位系統無法使用7G的RAM。 – 2012-03-29 14:54:11

+2

@PéterTörök - 確實如此,但是如果在32位機器上使用'-Xmx7g',則JVM將無法啓動。所以我不認爲這是問題。 – 2012-03-29 15:00:57

回答

1

我認爲,這裏發生的一切是你的程序的性質和你選擇

基本上GC設置的一個不幸後果,你的程序是創建節點的龐大的數字非常快,(在長相像)從來沒有釋放他們中的任何一個,直到運行結束。因此,每次GC運行時,它都必須跟蹤和撤出其正在處理的「發送」空間中的每個對象。這項工作與空間中物體的數量成正比。隨着您的應用程序的不斷髮展,空間不斷變大,物體數量不斷增加,並且GC將物體移動的時間越來越多。這可能會由於您正在運行CMS收集器而產生惡化,該收集器的開銷比吞吐量收集器要大。

這可能解釋了爲什麼你只使用25%的可用內存。但是,您從top得到的數字也有可能是誤導性的。我會更傾向於相信你從jconsole等人得到的數字。

我也打開GC日誌記錄,看看是否有什麼奇怪的事情發生。例如,您的應用程序的行爲可能會壓倒CMS收集器,並導致JVM切換到「停止世界」GC。 (我依稀記得聽到你採取一個大的性能損失,當這種情況發生,這可能足以導致JVM打GC開銷限制。)


那麼,你能做些什麼來改善的行爲運行此應用程序的JVM。我建議如下:

  • 嘗試使用吞吐量收集器,而不是CMS。
  • 如果您有一個可以使用的JVM,請嘗試使用新的G1收集器。
  • 將初始堆大小設置爲7千兆字節:使用-Xms7g

對GC日誌的仔細分析可能能夠提出其他嘗試的建議。

但是,你可能做的最好的事情是溝通這個(我希望)不切實際的基準。

+0

你也許沒錯。我注意到,在一分鐘內,內存使用率上升到25%,然後一分鐘後,它給了我錯誤。我會研究如何做日誌記錄,我會在沒有CMS收集器的情況下嘗試它(我使用它是因爲我在鏈接問題中讀到它可能允許我使用更多內存) – 2012-03-29 15:36:03

1

我絕不是專家,但這將是我的猜測。

如果不會做GC,直到它完全脫離的記憶,它會...

A)...消耗一個令人難以置信的大/不必要的大量內存

B)。 ..遭受巨大的性能影響的時間它實際上做GC。

因此,它確實在內存不存在之前執行GC,並且此GC進程無法跟上您正在創建的數百萬個節點並丟棄在可能較深的對象圖中。

+0

但是不是-XX:-UseGCOverheadLimit告訴它不要做GC?在這個問題中,我聯繫人寫道,它會導致問題不被檢查。此外,您還觸發了幾個問題,它如何判斷某些內容太大或不必要?它知道我可以在其他地方使用數據嗎?有沒有辦法關掉這個?如果問題沒有意義,我只是很遺憾JVM正在做的後臺任務。 – 2012-03-29 15:04:59

+0

「-UseGCOverheadLimit」的好處。我沒有一個好的答案。關於你的後續問題:這些東西也可以用'-XX'-選項來調整。你有例如'-XX:MaxHeapFreeRatio = 70'([source](http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html))。 – aioobe 2012-03-29 15:08:11

相關問題