java - 如果我有大量空闲内存分配给 JVM,为什么会得到 'java.lang.OutOfMemoryError: GC overhead limit exceeded'?

标签 java

我正在学习和测试一些图形库,并且遇到了一个奇怪的问题(但这不是特定于图形的,我很确定这是与 Java 相关的)。我收到:'java.lang.OutOfMemoryError:超出 GC 开销限制'。 I understand this error means that garbage collecting spending most of the cpu time and not returning any memory但我不知道如何解决这个问题。

基本上我(出于学习目的)想看看在内存中创建大量图形节点需要多长时间。我的系统正在运行美分,我有 7 GB 内存,但程序从未超过 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 添加到末尾时,它似乎可以工作。

最佳答案

我认为这里发生的情况是您的程序性质和您选择的 GC 设置的不幸后果

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

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

我还会打开 GC 日志记录,看看是否有奇怪的情况发生。例如,您的应用程序的行为可能会压垮 CMS 收集器,并导致 JVM 切换到 stop-the-world GC。 (我隐约记得听说发生这种情况时,性能会受到很大影响,这可能足以导致 JVM 达到 GC 开销限制。)


那么您可以采取什么措施来改进运行此应用程序的 JVM 的行为。我建议如下:

  • 尝试使用吞吐量收集器而不是 CMS。
  • 如果您有可用的 JVM,请尝试使用新的 G1 收集器。
  • 将初始堆大小也设置为 7GB:使用 -Xms7g

仔分割析 GC 日志也许能够建议尝试其他方法。

但也许你能做的最好的事情就是放弃这个(我希望)不切实际的基准。

关于java - 如果我有大量空闲内存分配给 JVM,为什么会得到 'java.lang.OutOfMemoryError: GC overhead limit exceeded'?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9927850/

相关文章:

java - 我如何通过java检查Excel文件中的单元格对于特定的第一个单元格值不为空

Java 无法解析从 feed 接收到的少数 unicode 字符

JavaFx 8 - 一个具有无法着色的边框并且能够在中间包含文本的类

java.lang.StringIndexOutOfBoundsException : when using PolyUtil. 解码

原始类型的 Java 迭代器

java - 此示例代码使用哪个版本的 Netty API?无法使用 3.2.x 解析 io.netty.handler.codec.http.DiskFileUpload

java - 多线程FTP下载

Java Swing - 如何更新 GUI 对象,即。来自同一包中子类的 JTextField 值

java - hibernate 给出两行相同的行而不是两行不同的行

重新定义默认 SSL 上下文时找不到 Java keystore