views:

394

answers:

4

Hi,

I have a code that does compression, encryption and checksum on a File Outputstream. Following is the code-

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);
}

As you can see here I am wrapping the "os" object for encryption, compression etc., and then re-assigning "os" variable with a different object (created using new) inside each of wrapOStreamWithCheckSum, wrapOStreamWithCipher and wrapOStreamWithCompress methods. I was wondering if this leads to memory leaks by any chance? What would actually happen to the older "os" objects created? To rephrase, there are 4 objects created using "new", but being re-assigned to the same "os" variable. I am finding it hard to understand because the new object creation/functioning itself is dependent on the old object internally.

Thanks,
-Keshav

A: 

Java has an automatic garbage collection mechanism, so you don't have to worry about not freeing the old objects that would otherwise create memory leak in C++. Basically you only have to make sure there are no dangling references to the objects you no more point to using the variable os.

Joy Dutta
+2  A: 

This is standard practice in Java and safe.

What happens is that each new object internally keeps a reference to the one passed in. This process is called "wrapping" or "delegation". When you close the last os, it will pass the method call on to the wrapped instance.

This way, one call will close them all and releasing the outermost os releases them all.

Aaron Digulla
+3  A: 

You only get memory leaks when an object is referenceable via the stack and you no longer want it to be in memory.

An example would be something like this:

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 ...
    }
}

The parser is no longer used but it is still reachable so it is, technically speaking, a memory leak (memory you no longer want used but cannot yet be reclaimed by the garbage collector).

To make it no longer used all you would have to do is set it to null or reassign it, then the memory can be collected.

In the case of wrapping, once the "root" object goes away, and as long as there are no other live references, all of the wrapping objects will be eligible for garbage collection. So, once the start meoth returns all of the objects created in there should e able to be collected.

TofuBeer
+2  A: 

All references to your outputstreams are local variables only. So after start() has terminated, there are no more references to the streams left and 'Big GC' will clean up.

If you really want to be sure and have an actual eclipse SDK and actual Java (6+) at hand, you can add a breakpoint to the os.close() line and inspect, if some unexpected objects hold references to your stream.

Andreas_D