We previously looked at resource cleanup in C++.
C++ has smart objects that release their resources when they go out of scope.
Java also has objects that release their resources when they are no longer used.
However, Java doesn't clean up its objects immediately after they stop being used.
This is because memory management can be an expensive operation, so batching it up with other memory cleanups can be useful.
However, since you can't assume that the smart objects have cleaned themselves up after they are no longer referenced, you need to explicitly tell them to clean up.
To make this easier, Java has a finally
block to go with its try
block, which is always executed, so you don't need separate cleanup code
in your success path and error path.
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Must be given 1 argument\n");
}
FileReader input;
try {
input = new FileReader(args[0]);
} catch (FileNotFoundException e) {
System.exit(1);
return; /* unreachable, but javac is dumb */
}
try {
char[] buf = new char[1024];
int ret;
while ((ret = input.read(buf, 0, buf.length)) != -1){
String s = new String(buf, 0, ret);
System.out.print(s);
}
} catch (IOException e) {
System.err.println("Failure while writing file contents\n");
} finally {
try {
input.close();
} catch (IOException e) {
}
}
}
}
Python also has a finally
block, with much less verbose syntax.
#!/usr/bin/python
import sys
if len(sys.argv) != 2:
sys.stderr.write("Must be given 1 argument\n")
try:
input = open(sys.argv[1], 'r')
while True:
d = input.read(1024)
if not d:
break
sys.stdout.write(d)
finally:
input.close()
A downside to the try...finally
idiom, is that it litters your code
with the cleanup code for resources you acquire. The RAII idiom from C++
handles this by putting the cleanup code in the destructors.
Unfortunately, while python may have deterministic resource cleanup from reference counting like C++ (Iron Python and Jython may not), you can't reliably write your own destructors in python.
To make this nicer, python has with
blocks, which allow the code for
cleanup to be kept with the code for allocation.
To use a with
block, you pass it an object that obeys the context
manager protocol. There are dedicated context manager objects, and
some other objects, such as open files, can be used as context managers.
With context managers, the code now looks like:
#!/usr/bin/python
import sys
if len(sys.argv) != 2:
sys.stderr.write("Must be given 1 argument\n")
with open(sys.argv[1], 'r') as input:
while True:
d = input.read(1024)
if not d:
break
sys.stdout.write(d)
To create your own context manager, you can write it like this:
#!/usr/bin/python
import contextlib
import sys
@contextlib.contextmanager
def open_input(path):
input = open(path, 'r')
try:
yield input
finally:
input.close()
if len(sys.argv) != 2:
sys.stderr.write("Must be given 1 argument\n")
with open_input(sys.argv[1]) as input:
while True:
d = input.read(1024)
if not d:
break
sys.stdout.write(d)
Java 7 supports try-with-resources which allows automatic closing of resources (e.g. streams) that implement AutoCloseable. The resources are local to the try-block.
There's a tutorial by Oracle at https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html for anyone interested.