对于启发式预计算表,我需要一个包含1504935936个条目的字节数组。这应该需要大约1.5 GB的内存。
public class Main{
public static void main(String[] args){
byte[] arr = new byte[1504935936];
}
}
为什么我有一个" OutOfMemoryError:Java堆空间" -Exception,如果我给程序2 GB的RAM
java -Xmx2048M Main
用
java -Xmx2153M Main
它有效。为什么需要那么多RAM?
答案 0 :(得分:5)
可能是因为Java堆正在被程序中的其他数据使用和分段。
该字节数组需要在Java堆空间内作为一个连续的1.5 GB内存块进行分配。 (这不是Java语言规范所要求的,但AFAIK是所有当前JVM实现的实际工作方式。)您的一些堆空间正在被消耗,并且 - 可能更重要的是 - 由您的程序之前发生的其他内存分配而碎片化分配那个大字节数组。那个java -Xmx2153M Main
可能是你需要多大才能使整个堆在你进入分配时留下连续的1.5 GB空间。
如果将大数组切割成100个较小的数组,大小为1/100,它可能适合较小的堆,因为它对堆碎片不敏感。
答案 1 :(得分:5)
这里的其他帖子有一些很好的信息,但他们错过了一个关键点:
获得一个好的内存分析器(最好是带有可视显示器的内存分析器)并将其附加到你的jvm。您将看到的是,现代的jvm没有一个大的堆空间,而是会有多个池(也称为代)。通常,“老一代”是最大的,但你也会有其他一些。 Together ,所有这些池应该大致相当于jvm允许的堆空间。
因此,您的“-Xmx2048M”设置不会产生具有单池的堆,该堆可以支持1.5GB阵列(如其他人所述,您需要一个连续的内存块用于数组,即一个完全包含在单个池/代中的内存块)。
答案 2 :(得分:4)
如果进程是作为32位进程执行的,那么大多数操作系统只为进程保留大约2GB的地址空间,另外2GB的地址空间被映射为内核的东西(这样当你的进程调用内核的东西时,你不必执行尽可能多的上下文切换。)
即使你的机器有8 GB的ram,或2 GB的2GB交换机,每个32位进程只能分配和处理2GB,除非你使用PAE或类似的。
这会导致一些问题。一,您可能没有足够的原始地址空间来存储所有分配的总大小。第二,你可能没有一个连续的内存块,这个内存大小就是你需要的数组--Java和其他几个VM环境使用单独的堆来存储不同类型的内存,例如,与gen 0分开的大对象堆,或者gen 1等对象。每个分区都会形成较小的连续区域。
在64位进程中,地址空间限制几乎消失,但是,您可能仍然没有足够的连续,可提交,允许Java的内存来满足请求。如果将Java设置为仅允许总共2GB的内存,则可能仍然无法找到足够的连续内存来满足请求。
请记住,该进程确实需要大量内存来存储程序的代码页,并且需要java运行时的内存。仅此一项可能只需几百兆的内存,具体取决于程序其余部分的需求。
在分配1个元素的字节数组时执行简单程序可能会有所帮助,并使用SysInternal VMMap检查内存,以了解内存开销的来源,不包括你的大量分配。
然后用你的大分配给它一个镜头,看看你得到了什么。
答案 3 :(得分:1)
jmap和jhat是发现谁在使用内存的哪些部分的好命令。我建议从堆转储开始并查看这些。只有部分可用内存在Java中分配给堆。运行VM和堆栈空间也需要内存。堆也分为几部分。当一个部分填充(终身代)时给出OutOfMemoryException
。堆分析器工具将帮助您确定究竟发生了什么。
对于更快的内容,您还可以在分配数组之前尝试检查这些值:
Runtime.getRuntime().totalMemory();
Runtime.getRuntime().freeMemory();
以下是一些更有用的链接,了解有关内存使用情况的更多信息:
答案 4 :(得分:1)
JVM内存空间分为几个区域。
使用选项-Xmx
设置java堆的大小,HotSpot的大小由四个空格构成,即Eden,Survivor 1和2以及终身。
要记住的是,第一棵树是指年轻空间,而休息则称为旧。
按default young space 消耗-Xmx
值的1/3。
然后表示声明-Xmx 2g时。那个年轻的空间将消耗超过600mb。
使用如此大的数据,您可以考虑使用Direct ByteBuffer,由Peter描述here:
IntBuffer arr = ByteBuffer.allocateDirect(size)
.order(ByteOrder.nativeOrder()).asIntBuffer();
arr.put(n, 1);// arr[n] = 1
arr.get(n); // arr[n]
Java - Heap vs Direct memory access
要诊断您的应用程序如何在HotSpot Oracle VM上使用Java堆,您可以找到随SDK一起提供的工具jstat。该工具可以为您提供有关应用程序发生情况的快速反馈。
在您的情况下,最有趣的选项是gccapacity
提供内存池生成和空间容量和gcutil
的数据,其中垃圾收集统计摘要强>
感谢gccapacity,您将找到以KB为单位的最大容量: