对于模拟,我希望能够有一个最多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错误?
答案 0 :(得分:1)
这里可能有很多因素。 arrayString
需要300MB的记忆。如果这大于你的最大内存大小,那就是你的问题。如果你的最小大小较小,那么当它试图获得它确实需要的内存时,这将减慢垃圾收集器的速度。
接下来,您将重复创建一个大约需要50KB的StringBuilder实例。这个空间可能会或可能不会被有效回收,但GC通常需要一些时间才能完全恢复更大的空间。因此,您只能使用最大内存的50%或更少,但GC正在永久地用于真正释放任何空间。 (与300MB相比,50KB并不是那么大,但我想我会提到它。)
最后一个问题是,如果GC开始耗时太长,它只会放弃并抛出OutOfMemoryError。即使理论上有足够的内存,也会发生这种情况。
最简单的解决方法是为它提供更多的最小和最大内存。不要认为这是因为你没有使用最大值,因此你可以安全地使用OOM。
值得注意的是,分配和释放记忆的速度很快就会伤害到你。交换一些需求较少的代码(较少new
和较小的大小)可以为GC提供更多时间来组织内存。尽管所需的工作相同,但由于期限较长,GC不会落后太多,因此它不会放弃并抛弃OOM。
编写GC的人总是在调整它们并尝试更好的权衡,因此确切地说明GC正在做什么,或明天他们将要做什么有点棘手。在这里,我只是想给你一些想法。