PipedInputStream/PipedOutStream处于紧密的“状态”;循环“::为什么会有";java.lang.OutOfMemoryError:java堆空间“;?
我正在试验PipedInputStream/PipedOutStream处于紧密的“状态”;循环“::为什么会有";java.lang.OutOfMemoryError:java堆空间“;?,java,garbage-collection,heap,Java,Garbage Collection,Heap,我正在试验PipedInputStream和PipedOutputStream,无法理解为什么下面的代码会导致Java堆耗尽问题。创建的所有瞬态String对象都应gc-ed。那么为什么我会得到OutOfMemoryError 我正在尝试写入和读取1000个String对象,每个对象的长度为100万个字符。即使使用-Xmx2g调用,下面的代码也会在中途失败。更重要的是跟踪: written string #453 read string #453 written string #454 Exce
PipedInputStream
和PipedOutputStream
,无法理解为什么下面的代码会导致Java堆耗尽问题。创建的所有瞬态String
对象都应gc-ed。那么为什么我会得到OutOfMemoryError
我正在尝试写入和读取1000个String
对象,每个对象的长度为100万个字符。即使使用-Xmx2g
调用,下面的代码也会在中途失败。更重要的是跟踪:
written string #453
read string #453
written string #454
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
。。。显示pipedOutStream
只是pipedOutStream
后面的一个String
对象。我不明白为什么垃圾收集无法回收所有必需的堆内存
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.");
}
}
}
import java.io.*;
导入java.util.*;
类工作者实现可运行的{
私有对象输出流oos;
私有管道数据流pis;
公共工作者()抛出异常{
this.pis=新的PipedInputStream();
this.oos=新对象输出流(新管道输出流(pis));
}
@凌驾
公开募捐{
试一试{
对于(int i=0;i<1000;i++){
oos.writeObject(aBigString());
System.out.printf(“写入字符串#%d\n”,i);
}
oos.flush();
oos.close();
}捕获(IOE异常){
抛出新的RuntimeException(例如getMessage());
}
}
私有静态字符串aBigString(){
StringBuffer sb=新的StringBuffer();
对于(int i=0;i<1000*1000;i++)
某人附加(“X”);
使某人返回字符串();
}
公共PipedInputStream getInput(){
返回这个.pis;
}
}
公共类FooMain{
公共静态void main(字符串args[])引发IOException,ClassNotFoundException{
工人=新工人();
(新线程(辅助线程)).start();
ObjectInputStream ois=新的ObjectInputStream(worker.getInput());
字符串记录=null;
int i=0;
试一试{
while(true){
记录=(字符串)ois.readObject();
System.out.printf(“读取字符串#%d”,i++);
}
}捕获(EOFEException e){
ois.close();
System.out.println(“完成”);
}
}
}
这与管道流无关。您遇到了对象流的一个经典陷阱。为了保留对象标识,流将保留通过它们的所有对象。如果需要将这些流用于大量对象,则需要定期调用ObjectOutputStream
上的reset()
(但请注意,对象标识不会在重置调用中保留)。这与管道流无关。您遇到了对象流的一个经典陷阱。为了保留对象标识,流将保留通过它们的所有对象。如果需要将这些流用于大量对象,则需要定期调用ObjectOutputStream
上的reset()
(但请注意,对象标识不会在重置调用中保留)。我建议下载Visual VM,安装所有插件,并在代码执行时将其附加到PID。它将向您显示内存、线程、对象、CPU等等。我建议下载Visual VM,安装所有插件,并在代码执行时将其附加到PID。它将向您显示内存、线程、对象、CPU以及更多信息。您是否尝试过StringBuilder而不是StrungBuffer?可能是perm gen填充,这是堆外的。32位JVM可能无法解决所有这些问题。只是想了解一下代码的要点:启动一个线程,用一千一百万的内存填充PipedInputStream
中的缓冲区。同时对字符进行反序列化的时候?这些字符串需要在内存中多次出现:创建时作为字符串本身,在管道中序列化时作为字节[],读取时作为字符串返回。在最坏的情况下,这可能需要多达6兆。@ThomasJungblut:为什么?它们不是gc-ed吗?@MarcusJuniusBrutus管道的输入字符串将在您reset()
时收集,但管道的内部缓冲区将在您取消引用时收集-只要它是活动的,它将携带1k字符串的信息(在最坏的情况下).您尝试过StringBuilder而不是StrungBuffer吗?可能是perm gen正在填充,这是堆外的。32位JVM可能无法解决所有这些问题。只是想了解一下代码的要点:启动一个线程,用一千一百万的内存填充PipedInputStream
中的缓冲区。同时对字符进行反序列化的时候?这些字符串需要在内存中多次出现:创建时作为字符串本身,在管道中序列化时作为字节[],读取时作为字符串返回。在最坏的情况下,这可能需要多达6兆。@ThomasJungblut:为什么?它们不是gc-ed吗?@MarcusJuniusBrutus管道的输入字符串将在您reset()
时收集,但管道的内部缓冲区将在您取消引用时收集-只要它是活动的,它将携带1k字符串的信息(在最坏的情况下).我只需要了解使用reset()和“不保留对象标识”的后果是什么;但也许这应该是另一个问题。我只需要理解使用reset()和“不保留对象标识”的后果是什么;但也许这应该是另一个问题。