首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >长FIFO队列应用程序的GC暂停调优

长FIFO队列应用程序的GC暂停调优
EN

Stack Overflow用户
提问于 2013-07-15 13:04:16
回答 7查看 1.2K关注 0票数 3

我有一个应用程序,它将一些数据放入一个长链接列表中,占用JVM中几乎所有的JVM内存。当插入一个新元素时,最后一个元素将被删除,因此列表的大小总是一个常量。当我将JVM内存大小设置为6GB时,我开始得到规则的GC暂停:3.4秒,大约每10秒发生一次。

我在Linux上使用Hotspot Java 1.7.0,64位,它有4核和16 on内存。传递以下JVM参数:-Xmx6g -XX:+PrintGC -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

请您提供一些更好的选项,将GC暂停时间减少到大约100毫秒左右。我试着自己寻找这样的选择,但没有成功。

资料来源如下:

代码语言:javascript
复制
    LinkedList<long[]> list = new LinkedList<long[]>();

    // initial fill in
    for(int i = 0; i < 16L*1024*1024; i ++) {
        list.add(new long[16]);
    }

    System.out.printf("total: %5.1f free: %5.1f\n",((float)Runtime.getRuntime().totalMemory())/(1024*1024*1024), ((float)Runtime.getRuntime().freeMemory())/(1024*1024*1024));

    // the main stuff
    for(;;) {
        list.removeFirst();
        list.add(new long[16]);
    }

更新:在下面的讨论中,我意识到人们试图对代码进行一些修改。因此,我需要更多地解释这个问题的背景。源示例是一个合成的不真实代码。它很好地说明了许多旧的gen对象的问题。我遇到这个问题时,我试图实现一个高度加载的缓存解决方案,其中有一些插入和驱逐策略。这常常导致旧一代垃圾的问题。我在这里的目标是使用JVM选项找到最好的解决方案。在这里,我不想考虑代码改进。我假设,如果有一种神奇的GC参数组合,这使得我的示例能够处理100 my以下的暂停,它也可能解决更一般的问题,或者至少为类似的情况提供一些提示。

EN

回答 7

Stack Overflow用户

发布于 2013-07-15 13:36:03

我会尝试用ArrayDequeue替换可怕的链接列表,特别是考虑到队列大小是恒定的。

非常长的链接列表会导致垃圾收集器的性能问题,因为垃圾收集器是递归实现标记的。收集器可能更喜欢使用可以迭代标记的大型数组。

更新

有一个模糊的GC调优参数可能有帮助:

  • CMSMarkStackSizeMax - CMS标记堆栈的最大大小,默认为4M。

如果您使这个参数更大,如果标记是深度递归的(因为它可能是针对一个巨大的链接列表),那么阻止CMS收集器切换到非增量模式就足够了。

但是,这样做会增加JVM的总体内存使用量。我的“信封背面”的想法是,要用16M元素标记链接列表,就需要在至少192兆字节的范围内标记一个标记堆栈。这需要乘以进行标记的GC线程的数量。

我问题的目标不是修改Java代码。想象一下,您有一个正确的java程序,它不会导致OutOfMemoryError。您必须在不更改代码的情况下找到正确的JVM参数。实际上,我了解这种暂停的原因,只是不知道如何对JVM进行调优,使暂停小于100 to。

我担心在这种情况下,你的目标很可能是无法实现的(不能承受上述)。如果应用程序对GC非常不友好,那么GC性能就会很差。

无论如何,你的更大的目标是(应该)用任何必要的方法来解决性能问题。把程序修好。在这种情况下,修复也可能有其他好处;例如,减少内存使用。

票数 2
EN

Stack Overflow用户

发布于 2013-07-16 07:26:51

我正在开发一个名为香蕉的原始集合库,它支持原语链接列表。您的用例--它是香蕉闪闪发光的理想用例,但它可以做得更多(包括可变长度块,您没有使用它,但可能在您的真实世界中使用)。

这是我的计算机上的基准测试的结果:

代码语言:javascript
复制
Banana : 1269 ms elapsed
Banana : total:   2.5 GB,  free:   0.5 GB, used =   2.1 GB, Banana reports that it's actually using   2.1 GB
Java : 13543 ms elapsed
Java : total:   6.2 GB,  free:   2.0 GB, used =   4.2 GB

你可以看到香蕉速度更快,内存也更少。(如果不首先运行香蕉函数,Java内存就会更好

代码语言:javascript
复制
Java : 14426 ms elapsed
Java : total:   5.8 GB,  free:   1.9 GB, used =   3.9 GB

但仍然没有靠近香蕉。

代码语言:javascript
复制
package net.yadan.banana.list;

public class LinkedListBenchmark {
  public static void main(String[] args) {
    banana();
    java();
  }

  public static void banana() {
    long t = System.currentTimeMillis();

    // initial list size 16m records, block size 32 (storage is int[], so we
    // need 32 ints to hold 16 longs)
    net.yadan.banana.list.LinkedList list = new LinkedList(16 * 1024 * 1024, 16 * 2, 0);

    // initial fill in
    for (int i = 0; i < 16L * 1024 * 1024; i++) {
      list.appendTail(32); // similar to java list.add() which appends to the
                           // end of the list
    }

    // the main stuff
    for (int i = 0; i < 16L * 1024 * 1024; i++) {
      list.removeHead(); // similar to java list removeFirst()
      list.appendTail(32); // similar to java list.add() which appends to the
                           // end of the list
    }

    System.out.println("Banana : " + (System.currentTimeMillis() - t) + " ms elapsed");
    float GB = 1024 * 1024 * 1024;
    long total = Runtime.getRuntime().totalMemory();
    long free = Runtime.getRuntime().freeMemory();
    System.out
        .printf(
            "Banana : total: %5.1f GB,  free: %5.1f GB, used = %5.1f GB, Banana reports that it's actually using %5.1f GB\n",
            total / GB, free / GB, (total - free) / GB, list.computeMemoryUsage() / GB);
  }

  public static void java() {

    long t = System.currentTimeMillis();

    java.util.LinkedList<long[]> list = new java.util.LinkedList<long[]>();

    // initial fill in
    for (int i = 0; i < 16L * 1024 * 1024; i++) {
      list.add(new long[16]);
    }

    // the main stuff
    for (int i = 0; i < 16L * 1024 * 1024; i++) {
      list.removeFirst();
      list.add(new long[16]);
    }

    System.out.println("Java : " + (System.currentTimeMillis() - t) + " ms elapsed");
    float GB = 1024 * 1024 * 1024;
    long total = Runtime.getRuntime().totalMemory();
    long free = Runtime.getRuntime().freeMemory();
    System.out.printf("Java : total: %5.1f GB,  free: %5.1f GB, used = %5.1f GB\n", total / GB, free / GB,
        (total - free) / GB);
  }
}
票数 2
EN

Stack Overflow用户

发布于 2013-07-18 13:39:29

对于大多数垃圾收集器来说,您的例子几乎就是病态的例子。解决这个问题的一个更好的方法是使用Disruptor,但是我从您的评论中看到,您不想要替代的设计建议。

如果您提供了一个GC日志,可能会有一些CMS调优选项来使事情变得更好,但是没有日志很难判断。停顿是由于FullGC引起的,还是由于备注阶段引起的?如果是FullGC,可能是CMS启动的时间不够早,无法跟上进度。

你真正要解决的问题是什么,因为这个设计的问题看起来有点疯狂?

如果您想拥有这样的设计模式,那么最好的JVM就是Azul。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17654964

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档