Java 8与Java 9之间的内存分配是否有所不同?

时间:2018-08-26 05:40:43

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

我想用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 运行相同的程序时,在得到异常之前,它只有大约 halfway (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版本以不同的方式管理/分配内存吗?

1 个答案:

答案 0 :(得分:2)

我发现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的时间更少。