2012-03-29 71 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

相关问题