当我运行以下程序(使用"java -Xmx151M -cp . com.some.package.xmlfun.Main"
运行)时:
package com.some.package.xmlfun;
public class Main {
public static void main(String [] args) {
char [] chars = new char[50 * 1024 * 1024];
}
}
我需要将最大内存增加到至少151M(-Xmx151M)。因此,当我增加数组大小时,需要增加限制:
为什么看起来java每个字符需要3个字节,而不是文档建议的2个字节?
此外,当我类似地创建长数组时,它似乎每个长度需要12个字节,而不是8个,使用int它需要6个字节而不是4个。通常看起来它需要array_size * element_size * 1.5
使用- javac \com\som\package\xmlfun\\*java
使用- java -Xmx151M -cp . com.some.package.xmlfun.Main
答案 0 :(得分:8)
我猜您所看到的内容可以通过JVM中的堆的组织方式轻松解释。
将参数-Xmx
传递给JVM时,您将定义最大堆大小应该是什么。但是,它与您可以分配的数组的最大大小没有直接关系。
在JVM中,垃圾收集器负责为对象分配内存和清理死对象。垃圾收集器决定它如何组织堆。
您通常会有一些名为 Eden space 的东西,然后是两个幸存者空间,最后是终身代。所有这些都在堆内,并且GC在它们之间划分最大堆。有关这些内存池的更多详细信息,请查看以下精彩答案:https://stackoverflow.com/a/1262474/150339
我不知道默认值是什么,它们可能确实依赖于您的系统。我刚刚检查(使用sudo jmap PID
)内存池如何在运行Ubuntu 64位和Oracle Java 7的系统上运行的应用程序中分割堆。该机器具有1.7GB内存。
在该配置中,我只将-Xmx
传递给JVM,GC按如下方式划分堆:
如果你有类似的发行版,这意味着151MB的最大连续块是在终身代,并且大约是100MB。由于数组是连续的内存块,并且您根本无法将对象跨越多个内存池,因此它解释了您所看到的行为。
您可以尝试使用垃圾收集器参数。在此处检查垃圾收集器参数:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
你的结果对我来说似乎很合理。
答案 1 :(得分:6)
在Java HotSpot VM中,堆分为“新一代”和“旧一代”。数组必须位于其中任何一个中。新旧一代尺寸比率的默认值为2。
(实际上表示old/new=2
)
因此,通过一些简单的数学计算,可以看出151MB堆可以拥有50.33MB的新一代和100.67MB的旧代。另外一个150MB的堆正好有100MB的老一代。您的数组+其他所有内容(例如args
)将耗尽100MB,因此产生OutOfMemoryError
。
我试图用
运行java -Xms150m -Xmx150m -XX:+PrintGCDetails Main > c.txt
来自c.txt
(...) Heap PSYoungGen total 44800K, used 3072K (addresses...) eden space 38400K, 8% used (...) from space 6400K, 0% used (...) to space 6400K, 0% used (...) ParOldGen total 102400K, used 217K (...) object space 102400K, 0% used (...) PSPermGen total 21248K, used 2411K (...) object space 21248K, 11% used (...)
这些空格并不完全等于我的计算,但它们就在附近。
答案 2 :(得分:1)
如果查看数据的大小(例如使用Visual GC),您会发现数组的大小确实是每个字符2个字节。
这里的问题是JVM尝试将整个数组放在旧一代堆中,并且这一代的大小受新旧一代大小的比例限制。
使用-XX:NewRatio=5
运行可以解决问题(默认值为2)。
答案 3 :(得分:0)
我会努力建立布鲁诺的答案。我现在试过这个代码:
public static void main(String[] args) throws IOException {
char [] chars = new char[50 * 1024 * 1024];
System.out.println(Runtime.getRuntime().freeMemory());
System.out.println(Runtime.getRuntime().totalMemory());
System.out.println(Runtime.getRuntime().maxMemory());
}
输出结果为:
38156248
143654912
143654912
很明显,40 MB可以用于JVM的其他目的。我最好的猜测是新一代的空间。