尽管有足够的可用内存,但巨大的数组会丢失内存

时间:2011-09-04 10:02:50

标签: java arrays memory

使用-Xmx1G标志提供一千兆字节的堆,以下工作方式如下:

public class Biggy {
    public static void main(String[] args) {
        int[] array = new int[150 * 1000 * 1000];
    }
}

数组应该代表大约600 MB。

但是,以下命令抛出OutOfMemoryError:

public class Biggy {
    public static void main(String[] args) {
        int[] array = new int[200 * 1000 * 1000];
    }
}

尽管数组应该代表大约800 MB,因此很容易适合内存。

遗失的记忆在哪里?

3 个答案:

答案 0 :(得分:19)

在Java中,堆中通常有多个区域(和子区域)。你有一个年轻而终身的地区,拥有大多数收藏家。大型阵列会立即添加到终端区域,但根据您的最大内存大小,将为年轻空间预留一些空间。如果你缓慢地分配内存,这些区域将会调整大小,但像这样的大块可能会失败,如你所见。

鉴于内存通常相对便宜(并非总是如此),我只会将最大值增加到您希望应用程序失败的程度,如果它曾经使用过那么多。

BTW:如果您有这样的大型结构,可以考虑使用直接内存。

IntBuffer array = ByteBuffer.allocateDirect(200*1000*1000*4)
                            .order(ByteOrder.nativeOrder()).asIntBuffer();

int a = array.get(n);
array.put(n, a+1);

编写它有点乏味,但有一个很大的优点,它几乎不使用堆。 (头顶不到1 KB)

答案 1 :(得分:3)

有足够的可用内存但不是一个连续的 内存块,根据数组的需要。

您可以使用使用较小块的其他数据结构吗? 内存,或几个较小的数组?

例如,以下代码适用于-Xmx1G

public class Biggy {
    public static void main(String[] args) {
        int [][]array = new int[200][];
        for (int i = 0; i < 200; i++) {
                array[i] = new int[1000 * 1000];
                System.out.println("i=" + i);
        }
    }
}

答案 2 :(得分:1)

堆内存分为三个空格:

  • 老一代
  • 幸存者空间
  • 伊甸园空间

一开始,这个对象将存在于旧一代中,并将在这里停留一段时间。

默认情况下,虚拟机会在每个集合中增大或缩小堆,以尝试将可用空间的比例保持为特定范围内每个集合的活动对象。此目标范围通过参数-XX:MinHeapFreeRatio =和-XX:MaxHeapFreeRatio =设置为百分比,并且总大小在-Xms以下由-Xms以上限制。

我的jvm中的默认比率是30/70,因此旧代的对象的最大大小是有限的(使用-Xmx1G)700Mb(顺便说一句,我在使用默认的jvm参数运行时得到相同的异常)。

但是你可以使用jvm选项调整代数。比如你可以 使用参数-Xmx1G -XX:NewRatio=10new int[200 * 1000 * 1000];运行您的课程将会成功。

据我所知,Java并不是为了在内存中保存大型对象而设计的。应用程序中内存的典型用法是一堆相对较小的对象的图形,通常只有在所有空格中空间不足时才会出现OutOfMemoryError。

下面是一些有用的(和有趣的阅读)文章:

Ergonomics in the 5.0 Java[tm] Virtual Machine

Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine