java - 即使没有内存不足,我也会遇到 java.lang.OutOfMemoryError 吗?

标签 java memory jvm out-of-memory

我正在阅读 Unveiling the java.lang.Out OfMemoryError我想知道我是否理解正确。如果 Java VM 抛出一个

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

意思是VM已经拒绝了数组的创建,因为它已经超过了一个预定义的限制(超过了VM的喜欢),而不是因为我已经用完了内存堆空间?

我可以说 java.lang.OutOfMemoryError: Requested array size exceeds VM limit 并不表示内存不足吗?

即使我无限制内存无处不在,Java VM 仍然会抛出一个java.lang.OutOfMemoryError: Requested array size exceeds VM limit 如果它不喜欢我创建一个 n 大小的数组的请求?

最佳答案

引用 Troubleshooting Guide for Java SE 6 with HotSpot VM

3.1.3 Detail Message: Requested array size exceeds VM limit (inline bold is mine):

The detail message Requested array size exceeds VM limit indicates that the application (or APIs used by that application) attempted to allocate an array that is larger than the heap size. For example, if an application attempts to allocate an array of 512MB but the maximum heap size is 256MB then OutOfMemoryError will be thrown with the reason Requested array size exceeds VM limit. In most cases the problem is either a configuration issue (heap size too small), or a bug that results in an application attempting to create a huge array, for example, when the number of elements in the array are computed using an algorithm that computes an incorrect size.


更新:在 @Pacerier 的鼓励下,我做了一些快速测试。我写了一个示例程序:

public class VmLimitTest {

    public static final int SIZE = 2;

    public static void main(String[] args) throws InterruptedException {
        while(true) {
            byte[] a = new byte[SIZE * 1024 * 1024];
            TimeUnit.MILLISECONDS.sleep(10);
        }
    }
}

并使用以下 JVM 选项运行它:

-Xms192m -Xmx192m -XX:NewRatio=2 -XX:SurvivorRatio=6 -XX:+PrintGCDetails

这就是他们的意思:

  • 整个堆是 192 MiB (-Xms192m -Xmx192m)
  • 新生代(eden + survivor)空间为 64 MiB,老年代为 128 MiB (-XX:NewRatio=2)
  • 每个幸存者空间(两个中的一个)为 8 MiB,因此 48 MiB 留给伊甸园(1:6 比例,-XX:SurvivorRatio=6)

在测试时我发现了以下内容:

  • 如果新创建的数组可以放入 eden(小于 48 MiB),程序运行正常
  • 令人惊讶的是,当数组大小超过 eden 大小时,但可以放入 eden 和一个幸存者空间(介于 48 和 56 MiB 之间),JVM 可以在 eden 和 survivor 上分配一个对象(重叠两个区域)。整洁!
  • 一旦数组大小超过 eden + single survivor(超过 56 MiB),新创建的对象将直接放置在老年代,绕过 eden 和 survivor 空间。这也意味着突然间一直执行完整的 GC - 非常糟糕!
  • 我可以轻松分配 127 MiB 的数据,但尝试分配 129 MiB 会抛出 OutOfMemoryError: Java heap space

这是底线——你不能创建一个比老年代还大的对象。尝试这样做会导致 OutOfMemoryError: Java heap space 错误。那么我们什么时候可以期待可怕的Requested array size exceeds VM limit

我尝试用更大的对象运行同一个程序。达到数组长度限制后,我切换到 long[] 并且可以轻松达到 7 GiB。 128 MiB 的最大堆声明 JVM 仍然抛出单个对象中有 8 GiB。我在具有 3 GiB 物理内存和 1 GiB 交换空间的 32 位 Linux 计算机上对此进行了测试。

话虽这么说,你应该永远不会遇到这个错误。该文档似乎不准确/已过时,但有一个结论是正确的:这可能是一个错误,因为创建如此庞大的数组非常罕见。

关于java - 即使没有内存不足,我也会遇到 java.lang.OutOfMemoryError 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9053144/

相关文章:

java - 将@Nullable 放在具有可为空返回类型的方法的何处?

c - 内存分配 : Why this C program works?

内存效率与处理器效率

java - 什么是 jvm 预分配异常?

java - Firebase 聊天应用程序 : query messages, 优化应为 OR 的查询,或改进 NoSQL 数据库结构

java - 在 Excel 中打开 xls 文件时使用 Apache POI 更新该文件

java - 如何在 this(...) 或 super(...) 之前编写 "insert"代码?

linux - 关于 Linux 内存类型的问题

Java - 为什么我在求和 double 类型时得到这个奇怪的答案

hadoop - 覆盖默认的JVM重用值