尽管有足够的可用内存,但巨大的数组会丢失内存
使用-Xmx1G
标志提供1千兆字节的堆,以下按预期工作:
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]; } }
尽管arrays应该代表大约800 MB,因此很容易适合内存。
丢失的记忆在哪里消失了?
在Java中,堆中通常有多个区域(和子区域)。 你有一个年轻而终身的地区,拥有大多数collections家。 大型arrays会立即添加到终端区域,但根据您的最大内存大小,将为年轻空间保留一些空间。 如果你慢慢分配内存,这些区域会resize,但像这样的大块可能会像你看到的那样失败。
鉴于内存通常相对便宜(并非总是如此),我只会将最大值增加到您希望应用程序失败的程度,如果它曾经使用过那么多。
顺便说一句:如果你有这样的大型结构,你可以考虑使用直接内存。
IntBuffer array = ByteBuffer.allocateDirect(200*1000*1000*4) .order(ByteOrder.nativeOrder()).asIntBuffer(); int a = array.get(n); array.put(n, a+1);
编写它有点乏味,但有一个很大的优点,它几乎不使用堆。 (头顶不到1 KB)
有足够的可用内存,但不是单个连续的内存块,如arrays所需。
您可以使用使用较小内存块或几个较小arrays的不同数据结构吗?
例如,以下代码适用于-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); } } }
堆内存分为三个空格:
- 老一辈
- 幸存者空间
- 伊甸园空间
一开始,这个对象将存在于老一代中,并将在这里停留一段时间。
默认情况下,虚拟机会在每个集合中增大或缩小堆,以尝试将可用空间的比例保持在特定范围内每个集合的活动对象。 该目标范围通过参数-XX:MinHeapFreeRatio =和-XX:MaxHeapFreeRatio =设置为百分比,并且总大小在-Xms以下由-Xms以上限制。
我的jvm中的默认比率是30/70,因此旧一代对象的最大大小是有限的(使用-Xmx1G)700Mb(顺便说一下,当使用默认的jvm参数运行时,我得到相同的exception)。
但是,您可以使用jvm选项调整代数。 例如,您可以使用参数-Xmx1G -XX:NewRatio=10
运行您的类-Xmx1G -XX:NewRatio=10
和new int[200 * 1000 * 1000];
将会成功。
从我可以说Java不是为了在内存中保存大对象而设计的。 应用程序中内存的典型用法是一堆相对较小的对象的图形,通常只有在所有空间中空间不足时才会得到OutOfMemoryError。
下面是一些有用的(和有趣的阅读)文章:
5.0 Java [tm]虚拟机中的人机工程学
使用5.0 Java [tm]虚拟机调整垃圾收集