1

I am experimenting with PipedInputStream and PipedOutputStream and can't understand why the following code would result in a Java Heap exhaustion problem. All transient String objects created should be gc-ed. Why then do I get an OutOfMemoryError ?

I am trying to write and read 1000 String objects each 1 million characters long. The below code fails about half-way through even when invoked with -Xmx2g. What's more the trace:

written string #453
read string #453
written string #454
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space

... reveals that the PipedInputStream is only one String object "behind" the PipedOutputStream. I don't see why garbage collection has failed to reclaim all necessary heap memory.

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.");
        }
    }
}
4

2 回答 2

6

这与管道流无关。您遇到了对象流的经典陷阱之一。为了保持对象身份,流将保留所有通过它们的对象。如果您需要将这些流用于大量对象,reset()则需要定期调用ObjectOutputStream(但请注意,对象身份不会在重置调用中保留)。

于 2013-06-18T11:35:18.497 回答
1

我建议下载 Visual VM,安装所有插件,并在代码执行时将其附加到您的 PID。它将向您展示内存、线程、对象、CPU 等等。

于 2013-06-18T11:35:33.077 回答