在while循环中的for循环中追加String时出现OOM错误

时间:2013-11-20 17:18:23

标签: java out-of-memory stringbuilder nested-loops

对于模拟,我希望能够有一个最多10000个时间步长的while循环。 在每个时间点,我想存储我的项目的实例变量。在我的实际代码中,项目是在每个时间点经历转换的对象,但是引起了这个OOM错误 因为我的代码中的String占用了我的堆内存而不是因为我的项目的转换。

在每个时间点,我想将我的项目的实例变量写出到StringBuilder,然后我转移到for循环外的一个String数组,但是在while(time)循环中。

我使用for循环遍历所有项目,并将所有实例变量附加在一行中以'%'分隔,以便能够分隔数据供以后使用。

如果我使用最多5000个时间步和1000个项目,我的代码工作正常。但是一旦我将时间步长增加到6000,我就会出现内存不足错误。

这是我的代码

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Test2 {
    public static void main(String[] args) throws IOException {

        int time = 0;
        int endTime = 6000;
        int items = 1000;
        File FILE = new File("test.txt");

        String[] arrayString = new String[endTime];

        while (time < endTime) {
            StringBuilder sb = new StringBuilder(50 * items);

            for (int i = 0; i < items; i++) {

                sb.append("Data from my items I want to store in a textfile.")
                  .append("%");         // '%' functions as a split-symbol between
                                        // two items that I later use as
                                        // delimiter for loading the text file
            }

            arrayString[(int) time] = sb.toString();
            sb.delete(0, sb.length());
            sb.setLength(50 * items);

            time++;
        }
        PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(
                FILE, true)));
        for (int j = 0; j < endTime; j++) {
            pw.println(arrayString[j]);
        }
        pw.close();
        System.out.println("Done");
    }
}

这是堆栈跟踪

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOfRange(Unknown Source)
    at java.lang.String.<init>(Unknown Source)
    at java.lang.StringBuilder.toString(Unknown Source)
    at package2.Test2.main(Test2.java:30)

和它指向的代码行

arrayString[(int) time] = sb.toString();


我是否达到了String数组中可以存储多少String的限制?或者是否有某种方法可以改进(我的代码的结构)来避免出现这个OOM错误?

1 个答案:

答案 0 :(得分:1)

这里可能有很多因素。 arrayString需要300MB的记忆。如果这大于你的最大内存大小,那就是你的问题。如果你的最小大小较小,那么当它试图获得它确实需要的内存时,这将减慢垃圾收集器的速度。

接下来,您将重复创建一个大约需要50KB的StringBuilder实例。这个空间可能会或可能不会被有效回收,但GC通常需要一些时间才能完全恢复更大的空间。因此,您只能使用最大内存的50%或更少,但GC正在永久地用于真正释放任何空间。 (与300MB相比,50KB并不是那么大,但我想我会提到它。)

最后一个问题是,如果GC开始耗时太长,它只会放弃并抛出OutOfMemoryError。即使理论上有足够的内存,也会发生这种情况。

最简单的解决方法是为它提供更多的最小和最大内存。不要认为这是因为你没有使用最大值,因此你可以安全地使用OOM。

值得注意的是,分配和释放记忆的速度很快就会伤害到你。交换一些需求较少的代码(较少new和较小的大小)可以为GC提供更多时间来组织内存。尽管所需的工作相同,但由于期限较长,GC不会落后太多,因此它不会放弃并抛弃OOM。

编写GC的人总是在调整它们并尝试更好的权衡,因此确切地说明GC正在做什么,或明天他们将要做什么有点棘手。在这里,我只是想给你一些想法。