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

Java 8和Java 9之间的内存分配是否存在差异?,java,memory,memory-management,dynamic-memory-allocation,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 =

我想在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选项运行程序时,我注意到Java8(1.8.0.131)和Java9(9.0.1)之间的行为差异

Java8的工作原理与我的预期非常接近。当我使用-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
但是,当我使用Java9运行同一个程序时,在得到异常之前,它只运行了大约一半(127MIB)。这是一致的。如果我将-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
我在文档中搜索了Java8和Java9之间内存管理方面的任何更改,但没有找到任何内容

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

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

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

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

-XX:UseParallelGC
您应该看到与Java8相同的行为。我链接的那篇文章有各种不同的选择,但我想你明白了。希望这有助于/回答你的问题,事实上,直到你提出这一点,我才意识到这一点,所以感谢你让我意识到这一点


编辑:根据他们自己的说法,他们说新的G1垃圾收集器是为高内存机器设计的,再次说明了为什么他们不会利用整个堆空间来减少GC处理时间。

为什么您关心Java 9?是的。Java11已经过时了。您是否在Java10和/或Java11早期版本中观察到了相同的行为?我确实在Java10中看到了相同的情况。我在示例中使用了Java9,因为这就是更改的开始。非常好的解释。尽管对你的答案有点质疑:在Java9中,如果我一次分配1MB,当大约一半的内存被使用时,我的内存就用完了。所以,如果我有256个MiB,它会停在126个MiB。但是,当我一次分配2MB时,168 Mib的内存就用完了。如果我分配3,它将停止在189 Mib。我想你已经找到了根本原因,这一定是由于gc的细节。是的,我想这个行为比我描述的更复杂。我认为真正完全理解g1gc工作原理的唯一方法是查看代码本身。