内存充足时出现java.lang.OutOfMemoryError错误

7
我正在尝试将一个2.5GB的txt文件读入我的应用程序。我正在运行Win7 x64,并且有43GB的可用内存(64GB中的)。我尝试调整-Xmx -XX:MaxParmSize -XX:ParmSize等参数,但这些都不影响错误。我还能尝试什么?这个错误似乎非常奇怪,因为我肯定有足够的堆空间可用。
Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit
    at java.util.Arrays.copyOf(Unknown Source)
    at java.lang.AbstractStringBuilder.expandCapacity(Unknown Source)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(Unknown Source)
    at java.lang.AbstractStringBuilder.append(Unknown Source)
    at java.lang.StringBuilder.append(Unknown Source)
    at j.utilities.IO.loadString(IO.java:187)
    at j.utilities.IO.loadString(IO.java:169)
    at city.PreProcess.main(PreProcess.java:78)

我正在跑步。
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) 64-Bit Server VM (build 23.5-b02, mixed mode)

非常感谢您的提前帮助。
============== 答案 ==============
好的,我刚刚测试了一下。
StringBuilder sb = new StringBuilder();
for ( int i=1; i<Integer.MAX_VALUE; i++ )
    sb.append("x");

and got

Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at java.util.Arrays.copyOf(Unknown Source)
...

因此,实际上是StringBuilder尝试构建比Integer.MAX_VALUE更大的数组。
如果感兴趣的话。
StringBuilder sb = new StringBuilder();
int i=1;
try {
    for ( ; i<Integer.MAX_VALUE; i++ )
        sb.append("x");
} catch ( OutOfMemoryError e ) {
    System.out.println(i); // OUTPUT: 1207959551
    System.out.println(Integer.MAX_VALUE); // OUTPUT: 2147483647
}

使用StringBuilder可以累加1,207,959,550个字符,远少于Integer.MAX_VALUE。

看起来你正在尝试分配一个数组。这个数组有多少个元素? - reprogrammer
PreProcess.java:78 的代码是什么? - Miserable Variable
j.utilities.IO是什么?如果它是你的代码,请粘贴IO.java:169 - Miserable Variable
StringBuilder内部是否使用数组? - dotwin
IO.loadString(IO.java:187) 是 "sb.append(line);"。 - dotwin
显示剩余2条评论
4个回答

10
您正在尝试分配一个太大的数组。这是因为您尝试创建一个非常长的字符串。由于数组是由整数索引的,所以一个数组不能超过Integer.MAX_VALUE个元素。即使您的堆大小非常大,也无法分配具有超过Integer.MAX_VALUE个元素的数组,因为您不能使用Integer来索引其元素。请参见Java数组是否有最大大小?了解更多详细信息。

StringBuilder内部是否使用数组?或者可能是将String解释为char数组? - dotwin
它应该保存一个 char[] - reprogrammer
2
是的,StringBuffer/StringBuilder确实将其数据存储在数组中。但是,如果您有一个大于2GB的字符串,则表示您做错了什么。 - MTilsted

3

你可以创建一个指定大小的StringBuilder,例如:

    StringBuilder sb = new StringBuilder(Integer.MAX_VALUE);

问题在于你试图读取一个比StringBuilder数组容量更大的文件。你有几个选项,例如:
1)你真的需要一次性将整个文件读入内存吗?如果是这样,你将不得不将它读入多个StringBuilder中。
2)按顺序处理文件。
3)将其读入压缩结构,并在需要时解压缩所需部分。

我正在使用Apache Camel读取文件,我认为它在内部将文件转换为StringBuilder。在这种情况下我该怎么办? - Muddassir Rahman

0

您应该查看Java命令的-Xmsn选项。

它指定了内存分配池的初始大小。

编辑:我看到您已经这样做了。


0

你可以用字符串缓存来保存数据,定义间隔并清空 StringBuffer


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接