-Xmx是一个硬限制?

Mar*_*tus 8 java java-7

这个SO答案澄清了有关-XmxJVM标志的一些事情.试着尝试我做了以下事情:

import java.util.List;
import java.util.ArrayList;

public class FooMain {

    private static String memoryMsg() {
        return String.format("%s. %s. %s"
                             , String.format("total memory is: [%d]",Runtime.getRuntime().totalMemory())
                             , String.format("free memory is: [%d]",Runtime.getRuntime().freeMemory())
                             , String.format("max memory is: [%d]",Runtime.getRuntime().maxMemory()));
    }

    public static void main(String args[]) {
        String msg = null;
        try {
            System.out.println(memoryMsg());
            List<Object> xs = new ArrayList<>();
            int i = 0 ;
            while (true) {
                xs.add(new byte[1000]);
                msg = String.format("%d 1k arrays added.\n%s.\n"
                                    ,  ++i
                                    , memoryMsg());
            }
        } finally {
            System.out.printf(msg);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用它编译javac FooMain.java.当我运行它的最大堆大小为500万字节时,我得到:

java -Xmx5000000 FooMain
total memory is: [5242880]. free memory is: [4901096]. max memory is: [5767168]
4878 1k arrays added.
total memory is: [5767168]. free memory is: [543288]. max memory is: [5767168].
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
          at java.lang.String.toCharArray(String.java:2748)
          at java.util.Formatter$FormatSpecifier.print(Formatter.java:3048)
          at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2744)
          at java.util.Formatter$FormatSpecifier.print(Formatter.java:2702)
          at java.util.Formatter.format(Formatter.java:2488)
          at java.util.Formatter.format(Formatter.java:2423)
          at java.lang.String.format(String.java:2792)
          at FooMain.memoryMsg(FooMain.java:7)
          at FooMain.main(FooMain.java:21)
Run Code Online (Sandbox Code Playgroud)

虽然这些数字足够接近,他们似乎并不十分准确(具备除total memory在年底达到准确max memory).特别是5368个1000字节的数组每个应该占用超过5000000甚至5242880字节.这些数字应该如何理解?

这是我正在使用的java:

java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) Server VM (build 24.80-b11, mixed mode)
Run Code Online (Sandbox Code Playgroud)

Pet*_*ter 6

查看OpenJDK源代码,确定最大堆大小的逻辑非常复杂,并且由很多变量决定.设置实际堆大小hotspot/src/share/vm/memory/collectorPolicy.cpp,它使用提供的-Xmx值作为输入,并使用以下代码将其对齐:

align_size_up(MaxHeapSize, max_alignment());
Run Code Online (Sandbox Code Playgroud)

align_size_up定义为:

#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1))
Run Code Online (Sandbox Code Playgroud)

max_alignment是虚拟内存页面大小和JVM垃圾收集卡大小的乘积.VM页面大小为4096字节,卡大小为512字节.将这些值插入后,实际的MaxHeapSize为6324224字节,这与您看到的数字很好地对应.

注意:我只是简单地查看了JVM代码,因此我可能错过了一些内容,但答案似乎与您所看到的相符.


Ste*_*n C 5

-Xmx是一个硬限制?

这取决于你的意思是"硬".如果你的意思是"不能被程序改变"那么是.如果你的意思是"精确",那么否.垃圾收集器使用的各种空间的大小是2字节的一些幂的倍数.显然,JVM向上而不是向下.

分配1000字节数组的示例程序

这些数字应该如何理解?

一个1000字节的Java数组不会占用1000个字节:

  • 有一个对象标题,包括32位length字段的空间.(通常总共3 x 32位).

  • 堆节点以2 ^ N个字节的倍数分配.(通常2 ^ 3 == 8)


那么你有什么导致的问题OutOfMemoryError.您可能认为这是因为堆已满.但是,在这种情况下,消息显示"超出GC开销限制".这意味着JVM检测到JVM在运行垃圾收集器的整个CPU时间上花费的比例过大.这就是杀死GC的原因......没有内存耗尽.

"GC开销限制"的基本原理是,当堆完全关闭时,GC会频繁运行,并且每次都会设法回收越来越少的内存.当你进入GC"死亡螺旋"时,最好快速拔出插头,而不是让应用程序磨到最终的失败点.

无论如何......这意味着你的启发式用于计算当堆满时分配了多少内存可能是不正确的.