【问题标题】:Are there differences in memory allocation between Java 8 vs. Java 9?Java 8 与 Java 9 之间的内存分配是否存在差异?
【发布时间】:2018-08-26 05:40:43
【问题描述】:

我想试验一下 Java 中的 -Xmx 选项,并创建了一个简单的测试程序,它每次分配 1 Mib 直到内存耗尽。

import java.util.Vector;

public class MemoryMuncher {
    static final int MiB = 1048576;
    static final int accellerator = 1;

    public static void main(String[] args) {
        Vector victor = new Vector();
        int i = 1;
        try {
            while (true) {
                byte roger[] = new byte[MiB * accellerator];
                victor.add(roger);
                Runtime rt = Runtime.getRuntime();
                System.out.printf("free: %,6d\t\ttotal: %,6d\t\tallocated: %,6d \n", rt.freeMemory()/MiB, rt.totalMemory()/MiB, i++*accellerator);
            }
        } catch (OutOfMemoryError e) {
            System.out.println(e);
        }
    }
}

当我使用 -Xmx 选项运行程序时,我注意到 Java 8 (1.8.0.131) 和 Java 9 (9.0.1) 之间的行为差​​异。

Java 8 的工作方式接近我的预期。当我使用 -Xmx256M 运行程序时,它会在抛出 OutOfMemoryError 之前分配 233MiB。

free:    361        total:    368       allocated:      1 
free:    360        total:    368       allocated:      2 
...
free:     12        total:    245       allocated:    232 
free:     11        total:    245       allocated:    233 
java.lang.OutOfMemoryError: Java heap space

但是,当我使用 Java 9 运行相同的程序时,它只获得了大约 中途 (127 MiB),然后我得到了异常。这是一致的。如果我将 -Xmx 更改为 512M 或 1024M,它只会达到该数量的一半(分别为 255 MiB 和 510 MiB)。我无法解释这种行为。

free:    250        total:    256       allocated:      1 
free:    247        total:    256       allocated:      2 
...
free:      2        total:    256       allocated:    126 
free:      1        total:    256       allocated:    127 
java.lang.OutOfMemoryError: Java heap space

我在文档中搜索了 Java 8 和 Java 9 之间内存管理方面的任何变化,但没有找到任何东西。

有没有人知道为什么 Java 9 在某些情况下会与以前的 Java 版本不同地管理/分配内存?

【问题讨论】:

  • 你为什么关心 Java 9?这是eol。 Java 11 已经发布。
  • 您是否在 Java 10 和/或 Java 11 早期版本之一上观察到相同的行为?
  • 我确实在 Java 10 中看到了同样的情况。我在示例中使用了 Java 9,因为这就是变化开始的地方。

标签: java memory memory-management dynamic-memory-allocation


【解决方案1】:

我发现 this 非常有趣的文章,介绍了 Java 9 如何计划以不同方式进行垃圾收集。

更具体地说,Java 9 计划使用 G1 垃圾收集器,它将内存划分为固定大小。您的代码可能正在做的是触发内存拆分为 Java 9 的两个固定大小的块。这样做的原因是它将节省另一半内存用于移动所有内容并“压缩”仍在使用的内存。但是,因为您只是继续使用内存,所以当使用 ~1/2 内存时它会立即中断。我不确定你的 CS 有多严格,但在我的学校里,我们学习了简单的垃圾技术,而 G1GC 让我想起的一个如此简单的技术是 stop-and-copy 技术,它在内存的两半之间切换分配的内存,同时它在执行此操作时会“压缩”内存。

在 Java 8 中,使用了 Parallel Collector,这与 G1GC 不同,因为它只关心吞吐量。因此,将使用接近 100% 的真实堆,代价是更长的 GC 时间。当然,这是两个版本之间的权衡,但您可以通过明确指定要使用的 GC 类型来解决这个问题。例如,如果在 Java 9 上您使用以下选项:

-XX:UseParallelGC

您应该看到与使用 Java 8 相同的行为。我链接的文章包含所有不同的选项,但我想您明白了。希望这有助于/回答您的问题,实际上直到您提出来我才意识到这一点,所以感谢您让我意识到这一点。

编辑: 根据Oracle 自己的说法,他们说新的 G1 垃圾收集器是为高内存机器设计的,这再次说明了为什么他们不会利用完整的堆空间来花费更少的时间GC-ing。

【讨论】:

  • 看来是这样。很好的解释。
  • 虽然有点挑战你的答案:在 Java 9 中,如果我一次分配 1 MB,当大约一半的内存被使用时,我的内存就会用完。因此,如果我有 256 MiB,它会停在 126 MiB。 但是,当我一次分配 2MB 时,我的内存在 168 Mib 时用完了。如果我分配 3,它将停在 189 Mib。我认为你有根本原因,这一定是由于gc的细节。
  • 是的,我想这种行为比我描述的要复杂。我认为真正完全理解 g1gc 工作原理的唯一方法是查看代码本身。
猜你喜欢
  • 2018-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-15
  • 2014-04-19
  • 1970-01-01
  • 2020-05-22
  • 2015-12-04
相关资源
最近更新 更多