包装多个输出流对象时的Java Outputstream行为

时间:2009-12-04 09:01:37

标签: java memory-leaks garbage-collection object outputstream

我有一个代码,可以在文件输出流上执行压缩,加密和校验和。以下是代码 -

private void start() {
    OutputStream os = null;
    try {
        os = new FileOutputStream("/some/file");
        os = wrapAllRequiredTransforms(os);

        //Write to os
    } finally {
        os.close();
    }
}

private wrapAllRequiredTransforms(OutputStream os) {
    if(checkSumRequired) {
        os = wrapOStreamWithCheckSum(os);
    }

    if(encryptionRequired) {
        os = wrapOStreamWithCipher(os);
    }

    if(compressRequired) {
        os = wrapOStreamWithCompress(os);
    }
}

private OutputStream wrapOStreamWithCheckSum(OutputStream os) throws Exception {
    os = new DigestOutputStream(os, MessageDigest.getInstance("MD5"));
    return os;
}

private OutputStream wrapOStreamWithCipher(OutputStream os) throws Exception {
    SecretKeySpec secretKeySpec = new SecretKeySpec(//SomeKey, encryptionAlgorithm);
    Cipher cipher = Cipher.getInstance(encryptionAlgorithm); 
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
    return new CipherOutputStream(os, cipher);
}

private OutputStream wrapOStreamWithCompress(OutputStream os) throws Exception {
    return new GZIPOutputStream(os);
}

正如你在这里看到的,我将包装“os”对象进行加密,压缩等,然后在每个wrapOStreamWithCheckSum,wrapOStreamWithCipher和wrapOStreamWithCompress方法中使用不同的对象(使用new创建)重新分配“os”变量。我想知道这是否会导致内存泄漏?创建旧的“os”对象会发生什么?换句话说,有4个对象使用“new”创建,但被重新分配给相同的“os”变量。我发现很难理解,因为新的对象创建/功能本身依赖于内部的旧对象。

4 个答案:

答案 0 :(得分:3)

当一个对象可以通过堆栈引用而你不再希望它在内存中时,你只会遇到内存泄漏。

一个例子是这样的:

public class Main
{
    private static CommandLineArgumentParser parser;

    public static void main(final String[] argv)
    {
        parser = new CommandLineArgumentParser(argv);
        ... use the parser
        ... never use the parser again ....
        ... do a bunch of work ...
    }
}

解析器不再使用,但它仍然可以访问,因此从技术上讲,它是一个内存泄漏(你不再需要使用但不能被垃圾收集器回收的内存)。

要使其不再使用,您只需将其设置为null或重新分配,然后就可以收集内存。

在包装的情况下,一旦“root”对象消失,并且只要没有其他实时引用,所有包装对象都将有资格进行垃圾回收。因此,一旦启动meoth返回在那里创建的所有对象应该能够被收集。

答案 1 :(得分:2)

这是Java中的标准做法,也是安全的。

每个新对象在内部保留对传入的对象的引用。这个过程称为“包装”或“委托”。当你关闭最后一个os时,它会将方法调用传递给包装的实例。

这样,一个调用将关闭它们并释放最外面的os将它们全部释放。

答案 2 :(得分:2)

对输出流的所有引用仅为局部变量。因此,在start()终止后,不再有对流的引用,并且'Big GC'将被清除。

如果你真的想确定并拥有一个真正的eclipse SDK和实际的Java(6+),你可以在os.close()行添加一个断点并检查,如果一些意外的对象持有对你的引用流。

答案 3 :(得分:0)

Java具有自动垃圾收集机制,因此您不必担心不会释放旧对象,否则会在C ++中造成内存泄漏。基本上,您只需要确保没有对使用变量os不再指向的对象的悬空引用。