我正在试验PipedInputStream
和PipedOutputStream
,并且无法理解为什么以下代码会导致Java堆耗尽问题。创建的所有临时String
对象都应为 gc -ed。为什么我会得到OutOfMemoryError
?
我正在尝试编写和读取每100万个字符长的1000个String
个对象。即使用-Xmx2g
调用,下面的代码也会失败一半。更重要的是:
written string #453
read string #453
written string #454
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
...显示PipedInputStream
只是String
背后的一个PipedOutputStream
对象。我不明白为什么垃圾收集无法回收所有必要的堆内存。
import java.io.*;
import java.util.*;
class Worker implements Runnable {
private ObjectOutputStream oos;
private PipedInputStream pis;
public Worker() throws IOException {
this.pis = new PipedInputStream();
this.oos = new ObjectOutputStream(new PipedOutputStream( pis ));
}
@Override
public void run() {
try {
for (int i = 0 ; i < 1000 ; i++) {
oos.writeObject(aBigString());
System.out.printf("written string #%d\n", i);
}
oos.flush();
oos.close();
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
private static String aBigString() {
StringBuffer sb = new StringBuffer();
for (int i = 0 ; i < 1000*1000 ; i++)
sb.append("X");
return sb.toString();
}
public PipedInputStream getInput() {
return this.pis;
}
}
public class FooMain {
public static void main(String args[]) throws IOException, ClassNotFoundException {
Worker worker = new Worker();
(new Thread(worker)).start();
ObjectInputStream ois = new ObjectInputStream(worker.getInput());
String record = null;
int i = 0;
try {
while (true) {
record = (String) ois.readObject();
System.out.printf("read string #%d", i++);
}
} catch (EOFException e) {
ois.close();
System.out.println("done.");
}
}
}
答案 0 :(得分:6)
这与Piped流无关。你正在遇到Object流的经典陷阱之一。为了保留对象标识,流将保持所有对象通过它们。如果您需要将这些流用于大量对象,则需要定期在reset()
上调用ObjectOutputStream
(但请注意,重置调用不会保留对象标识)。
答案 1 :(得分:1)
我建议下载Visual VM,安装所有插件,并在代码执行时将其附加到PID。它会向你显示内存,线程,对象,CPU等等。