Java 8 与 Java 9 之间的内存分配是否存在差异?

Are there differences in memory allocation between Java 8 vs. Java 9?

我想试验 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 在某些情况下 manage/allocate 的记忆与以前的 Java 版本不同?

我发现 this 一篇关于 Java 9 计划如何以不同方式进行垃圾收集的非常有趣的文章。

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

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

-XX:UseParallelGC

您应该看到与使用 Java 时相同的行为 8. 我链接的文章有所有不同的选项,但我想您明白了。希望这个 helps/answer 你的问题,直到你提出来我才意识到这一点,所以谢谢你让我意识到这一点。

编辑: 根据 Oracle 他们自己的说法,他们说新的 G1 垃圾收集器是为高内存机器设计的,再次给出了他们不使用满堆 space 以减少 GC 时间。