PipedInputStream/PipedOutputStream紧密"循环"::为什么"java.lang.OutOfMemoryError:Java堆空间"?

Mar*_*tus 1 java heap garbage-collection

我正在尝试PipedInputStream并且PipedOutputStream无法理解为什么以下代码会导致Java堆耗尽问题.String创建的所有临时对象都应该是gc -ed.为什么我会得到一个OutOfMemoryError

我试图写String每1000 万个字符长的1000个对象.即使在调用时,下面的代码也会失败一半-Xmx2g.更多的痕迹:

written string #453
read string #453
written string #454
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
Run Code Online (Sandbox Code Playgroud)

......揭示了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.");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

jta*_*orn 6

这与Piped流无关.你正在遇到Object流的经典陷阱之一.为了保留对象标识,流将保持通过它们的所有对象.如果你需要使用这些流了大量的对象,你需要定期调用reset()ObjectOutputStream(但要注意对象标识,不会保留重置调用).