为什么我会得到';java.lang.OutOfMemoryError:超出GC开销限制';如果我有大量的空闲内存给JVM?

为什么我会得到';java.lang.OutOfMemoryError:超出GC开销限制';如果我有大量的空闲内存给JVM?,java,Java,我正在学习和测试一些图形库,并遇到了一个奇怪的问题(但这并不是特定于图形的,我很确定这与Java相关)。我得到:“java.lang.OutOfMemoryError:超出了GC开销限制”。但我不知道如何解决这个问题 基本上,我(出于学习目的)想看看在内存中创建大量的图需要多长时间。我的系统运行的是美分,我有7 Gig的ram,但是这个程序从来没有超过25%(我可以通过“top”看到),即使我通过运行“java-jar jungtester.jar-Xmx7g-XX:+useConcmarksw

我正在学习和测试一些图形库,并遇到了一个奇怪的问题(但这并不是特定于图形的,我很确定这与Java相关)。我得到:“java.lang.OutOfMemoryError:超出了GC开销限制”。但我不知道如何解决这个问题

基本上,我(出于学习目的)想看看在内存中创建大量的图需要多长时间。我的系统运行的是美分,我有7 Gig的ram,但是这个程序从来没有超过25%(我可以通过“top”看到),即使我通过运行“java-jar jungtester.jar-Xmx7g-XX:+useConcmarkswipegc-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));
    }

}
导入edu.uci.ics.jung.graph.DirectedGraph;
导入edu.uci.ics.jung.graph.DirectedSparseGraph;
公共班机{
/**
*@param args
*/
公共静态void main(字符串[]args){
//TODO自动生成的方法存根
System.out.println(“开始…”);
long startTime=System.currentTimeMillis();
DirectedGraph graph=新的DirectedSparseGraph();;
图.addVertex(1);
图.添加顶点(2);
图.附录(1,1,2);
对于(int i=0;i<1000000;i++){
图1.addVertex(i);
//System.out.println(i+)表示我们是“+(float)i/1000000+%done.”;
}
long-endTime1=System.currentTimeMillis();
System.out.println(“在”+(endTime1-startTime)中添加1000000完成);
对于(int i=1000001;i<10000000;i++){
图1.addVertex(i);
图.附录(i,i,i-1000000);
System.out.println(i+)表示我们是“+(float)i/100000000+%done.”;
}
long-endTime=System.currentTimeMillis();
System.out.println(“需要”+(endTime-startTime));
}
}

更新:我让它工作,我不知道为什么,但顺序很重要。我上面的命令在我放入-jar之后没有任何效果,但是当我将-jar添加到末尾时,它似乎起了作用。

我绝不是专家,但这是我的猜测

如果它在内存完全耗尽之前不执行GC,它将

A) 。。。消耗难以置信的大量/不必要的内存

B) 。。。在实际执行GC时,它会受到巨大的性能影响


因此,它确实会在内存耗尽之前执行GC,而这个GC过程无法跟上您在一个可能很深的对象图中创建和丢弃的数百万个节点。

我认为,这里发生的事情是由于您的程序的性质和您选择的GC设置而造成的不幸后果

基本上,您的程序正在非常快速地创建大量节点,并且(看起来)在运行结束之前不会释放任何节点。因此,每次GC运行时,它都必须跟踪并撤离它正在处理的“from”空间中的每个对象。这项工作与空间中物体的数量成正比。随着应用程序的发展,空间不断变大,对象数量不断变大,GC移动对象所花费的时间越来越多。运行CMS收集器的开销比吞吐量收集器的开销更大,这可能会加剧这种情况

这可能解释了为什么您似乎只使用了25%的可用内存。但是,您从
top
获得的数字也可能具有误导性。我更倾向于相信你从
jconsole
等网站上得到的数字

我还打开GC日志,看看是否有奇怪的事情发生。例如,应用程序的行为可能会使CMS收集器无法承受,并导致JVM切换到停止世界GC。(我模模糊糊地记得,当这种情况发生时,您的性能会受到很大的影响,这可能足以导致JVM达到GC开销限制。)


那么,您可以做些什么来改进运行此应用程序的JVM的行为呢。我建议如下:

  • 尝试使用吞吐量收集器而不是CMS
  • 如果您有一个支持的JVM,请尝试使用新的G1收集器
  • 将初始堆大小也设置为7GB:使用
    -Xms7g
仔细分析GC日志可能会建议尝试其他事情


但是,最好的办法可能是放弃这个(我希望是)不切实际的基准测试。

使用jconsole(如果您有X可用)在应用程序运行时监视JVM,它将向您显示发生了什么。Top在显示JVM实时统计数据方面并不像jconsole那么精细。您是在32位还是64位机器上运行它?32位系统不能使用7G内存。@PéterTörök-这是真的,但是如果在32位机器上使用
-Xmx7g
,JVM将无法启动。所以我不认为这是问题所在。对不起,我正在命令行上运行它(我没有图形可用)@PéterTörök它是一台64位机器。@learningJava是一台64位JVM吗?但是-XX:-UsegcOverdeLimit告诉它不要做GC吗?我在问题中写道,这会导致问题得不到解决。此外,您还引发了一些问题,它如何判断某个东西太大或不必要?据它所知,我可能在其他地方使用这些数据?有去蜀国的路吗