-Xmx是一个硬限制吗?

8

这篇SO答案澄清了关于-Xmx JVM标志的一些事情。为了尝试实验,我做了以下操作:

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);
        }
    }
}

使用 javac FooMain.java 进行编译。当我用最大堆大小为5百万字节运行时,出现以下结果:

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)

虽然数字很接近,但它们似乎并不是非常精确(除了最后的总内存恰好达到最大内存的情况除外)。特别是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)

也许在此期间垃圾回收已经启动了... - Robert Niestroj
1
@RobertNiestroj 它无法这样做,因为所有的引用都被保留。 - André Stannek
1
@RobertNiestroj 为什么需要进行垃圾回收?我正在将数组添加到List中。没有引用超出作用域。 - Marcus Junius Brutus
总内存并不能说明什么,因为它是JVM使用的所有内存,而Xmx只是声明堆空间的最大值。尽管如此,我同意5468个1000字节数组不应该适合5000000字节的堆。非常有趣... - André Stannek
我已经在Mac上运行了这段代码,它总是输出约4700。 - andy
显示剩余6条评论
3个回答

7

浏览OpenJDK源代码,用于确定最大堆大小的逻辑非常复杂,并由许多变量确定。实际堆大小在hotspot/src/share/vm/memory/collectorPolicy.cpp中设置,该文件使用提供的-Xmx值作为输入,并使用以下代码将其对齐:

align_size_up(MaxHeapSize, max_alignment());

align_size_up的定义如下:

#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1))

而max_alignment是虚拟内存页面大小和JVM垃圾收集卡大小的乘积。VM页面大小为4096字节,卡大小为512字节。将这些值插入计算可得到实际的MaxHeapSize为6324224字节,与您看到的数字相符。

注意:我仅简要查看了JVM代码,因此我可能会漏掉一些内容,但答案似乎与您看到的情况相符。


5
-Xmx是否是硬限制?
这要看你所说的“硬”是什么意思。如果指“程序不能更改”,那么是。如果指“精确的”,那么不是。垃圾收集器使用的各种空间大小是字节的某个2的幂次倍。显然,JVM向上舍入而不是向下舍入。
1000字节数组的示例程序应如何理解?
1000字节的Java数组的实际占用空间并不是1000字节:
- 有一个对象头,包括32位长度字段的空间。(通常总共为3 * 32位)。 - 堆节点按2 ^ N字节的倍数分配。(通常为2 ^ 3 == 8)
然后你就会问,是什么导致了OutOfMemoryError?你可能会认为是因为堆完全满了。然而,在这种情况下,消息显示“GC overhead limit exceeded”。这意味着JVM已检测到JVM在运行垃圾收集器时耗费了太多的CPU时间。这才是GC被中止的原因...而不是内存用尽。
“GC overhead limit”的理由是当堆接近满时,GC会频繁运行,并且每次成功回收的内存越来越少。当你进入GC“死亡螺旋”时,最好快速关闭程序,而不是让应用程序继续运行到最后失败的点。
总之...这意味着你计算堆满时分配了多少内存的启发式方法可能不正确。

1
你的问题已经被其他人回答了,但出于兴趣我已经计算过了 :-)
这里声明了 这里
该值必须是大于2 MB的1024的倍数。
5000000不是1024的倍数。我的猜测是参数会被舍入为1024的倍数,其他答案也证实了这一点。你的总内存(不仅仅是声明的堆空间)是5767168,即 5632 * 1024。它在运行时从初始的5242880扩大以适应不断增长的堆空间。请注意,-Xmx 声明了最大值,因此不一定会立即分配。通过 这个来源 的帮助,我们可以近似计算您的内存使用情况(假设是64位系统):
  • 4878000字节用于字节
  • 4878 * 24 = 117072字节数组开销
  • 其他创建的对象和字符串需要一些字节
  • 由于垃圾回收,内存会有一些波动(至少每次迭代创建的字符串可能会被丢弃)

因此,数组占用了4995072个字节,这已经是1024的倍数了(巧合吗?)。但其他对象还需要开销。因此,堆空间是4995072的某个1024倍数,并且低于5767168。考虑到最后的可用空间,这为我们留下了228808字节的其余堆和非堆内存使用,听起来像一个合理的数字。

最后说一句关于最后剩余的可用空间。JVM不一定会在崩溃之前填满所有内存。如果内存接近耗尽,垃圾回收会更频繁地运行。如果这占用了整体运行时间的一定百分比,JVM会退出,这就是你的情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接