【问题标题】:Why SerialGC is faster than ParallelGC when data set is small?为什么数据集较小时 SerialGC 比 ParallelGC 快?
【发布时间】:2018-02-16 13:24:22
【问题描述】:

来自doc 中的选择收集器一章:

如果应用程序的数据集很小(最多大约 100 MB),则选择带有选项 -XX:+UseSerialGC 的串行收集器。

串行收集器使用单个线程来执行所有垃圾收集工作,这使得它相对高效,因为线程之间没有通信开销。

我对此进行了一些测试,

public class Example {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        Map<Integer, Object> map = new HashMap<>();
        for (int count = 0; count < 60000; count++) {
            map.put(count, new Object());
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

使用 SerialGC:

-Xms5m -Xmx5m -XX:+UseSerialGC -XX:+PrintGC

结果大约是 50 毫秒。

使用 ParallelGC:

-Xms5m -Xmx5m -XX:+UseParallelGC -XX:+PrintGC

结果大约是 6000 毫秒。

我知道在 ParallelGC 中线程之间的通信可能需要一些时间,在这种情况下,SerialGC 比 ParallelGC 快得多还有其他原因吗?

【问题讨论】:

    标签: java garbage-collection jvm


    【解决方案1】:

    除了线程,我能想到的另一个原因是:

    当串行垃圾收集运行时,其他任何东西都无法运行(也称为“停止世界”)。但这有一个好处:它将垃圾收集所花费的工作量保持在最低限度。

    几乎任何类型的并行或并发垃圾回收都必须做大量额外的工作,以确保对堆的所有修改对于其余代码来说都是原子的。它必须停止那些依赖于特定变化的事情,而不是仅仅停止一段时间,然后停止足够长的时间来执行特定的变化。然后它让该代码再次开始运行,到达下一个要进行更改的点,停止依赖它的其他代码片段,等等。

    【讨论】:

    • 使用 SerialGC,发生了五次 GC。使用ParallelGC,有非常非常多次GC,但似乎没有空间可以回收。
    • @Utsav 串行和并行收集器都是 StW 收集器。 CMS 和 G1 是并发(或大部分并发)收集器。
    【解决方案2】:

    第一

    5MB 堆本质上是退化的情况。通过大量的调整,JVM 可以在这种情况下工作,但默认情况下很容易遇到问题。

    5MB 堆大小并不意味着您可以分配 5MB 的对象,因为在任何给定时间年轻代都会部分为空,换句话说,它会占用您的内存预算。 p>

    除非您有充分的理由使用这些内存限制,否则请选择更大的值,默认值是为更典型的工作负载选择的,在这些情况下可能根本无法正常工作。

    第二

    设置 JVM 参数也可能会更改其他默认参数,因此您设置的参数并不是唯一更改的内容。

    为了获得更好的图片,您可以按如下方式比较计算的标志:

    diff -U 0 <(java -Xms5m -Xmx5m -XX:+UseSerialGC -XX:+PrintFlagsFinal) <(java -Xms5m -Xmx5m -XX:+UseParallelGC -XX:+PrintFlagsFinal)
    

    【讨论】:

      【解决方案3】:

      (一开始是评论,但变得太长了)
      - 您是否使用 jmh 作为基准?
      - 你的机器上有多少个硬件线程?

      如果 JIT 已成功完成工作,您的整个循环将是一个 nop,因为数据不会在任何地方使用。 逃逸分析可能也介入了,但我不这么认为,对象的数量太大了,我猜。
      换句话说,我不确定你正在测量你认为正在测量的东西。改用System.gc 可能会更好。虽然这只是我所知道的所有收藏家都听从号召的建议。
      另请注意,串行收集器和并行收集器之间的唯一区别是并行收集器使用所有可用的 cpu 来运行,而串行只使用一个。它们都不是并发的,它们都是 StW 压缩收集器(对于老一代)。 (注意顺便说一句,所有年轻一代的收集器(C4 除外)都是 StW 复制收集器)。
      我建议改用 jmh 编写基准测试,但目前尚不清楚如何为此编写严格的基准测试。
      另一个建议是使用性能测试系统(如果有的话)并使用相同的场景与差异收集器一起运行它并分析 gc 日志。然后你会得到一个有意义的比较。

      【讨论】:

      • 感谢您的建议。我没有使用 jmh 或其他性能测试系统。我对 jvm 以及垃圾收集都很陌生。现在,我只需要对这些东西有一个基本的了解......
      猜你喜欢
      • 1970-01-01
      • 2011-06-21
      • 2012-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多