You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
9.4 KiB

Infer reports resource leaks in C, Objective-C and Java. In general, resources
are entities such as files, sockets, connections, etc, that need to be closed
after being used.
### Resource leak in C
This is an example of a resource leak in C code:
```c
-(void) resource_leak_bug {
FILE *fp;
fp=fopen("c:\\test.txt", "r"); // file opened and not closed.
}
```
### Resource leak in Java
For the remaining of this section, we will consider examples of resource leaks
in Java code.
TIP: A common source of bugs is <b>exceptions skipping past close()
statements</b>. That is the first thing to look for if INFER reports a potential
resource leak.
### Basics and Standard Idiom
Some objects in Java, the <i>resources</i>, are supposed to be closed when you
stop using them, and failure to close is a <i>resource leak</i>. Resources
include input streams, output streams, readers, writers, sockets, http
connections, cursors, and json parsers.
The standard idiom is
```java
// Standard idiom
Allocate resource
try {
do some stuff
} finally {
close resource
}
```
or more for example,
```java
// Standard Idiom
public static void foo () throws IOException{
FileOutputStream fos = new FileOutputStream(new File("whatever.txt"));
try {
fos.write(7);
} finally {
fos.close();
}
}
```
and you should use the standard idiom for the most part, when you don't want to
return the resource to the surrounding context.
Sometimes people just leave out close(), and that is a bug, but more typically
exceptional paths are the root of the problem, as in
```java
// leak because of exception
public static void foo () throws IOException {
FileOutputStream fos = new FileOutputStream(new File("whatever.txt"));
fos.write(7); //DOH! What if exception?
fos.close();
}
```
where an exception in fos.write will cause execution to skip past the close()
statement.
#### Multiple Resources Bugs
We can deal with multiple resources correctly and simply just by nesting the
standard idiom.
```java
// Two Resources nested
public static void foo() throws IOException {
FileInputStream fis = new FileInputStream(new File("whatever.txt"));
try {
FileOutputStream fos = new FileOutputStream(new File("everwhat.txt"));
try {
fos.write(fis.read());
} finally {
fos.close();
}
} finally {
fis.close();
}
}
```
Bugs often occur when using multiple resources in other ways because of
exceptions in close() methods. For example,
```java
// Classic Two Resources Bug
public static void foo() throws IOException {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(new File("whatever.txt"));
fos = new FileOutputStream(new File("everwhat.txt"));
fos.write(fis.read());
} finally {
if (fis!=null) fis.close();
if (fos!=null) fos.close();
}
}
```
Here, if there is an exception in the call to fis.close() execution will skip
past fos.close(); a leak.
Another way, besides the standard idiom, to deal with this problem is to swallow
exceptions.
```java
// Two Resources Fix 1
public static void foo() throws IOException {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(new File("whatever.txt"));
fos = new FileOutputStream(new File("everwhat.txt"));
fos.write(fis.read());
} finally {
try {
if (fis!=null) fis.close();
} catch (Exception e) {}; // Exception swallowing
if (fos!=null) fos.close();
}
}
```
You can also swallow the exception on the output stream. Some people prefer not
to swallow output stream exceptions, and also flush before closing.
http://code.google.com/p/guava-libraries/issues/detail?id=1118
Notice that the nested standard idiom does not need the checks for null, which
are in there in this case to protect against the case when one of the
allocations throws an exception, in which case one would get a
NullPointerException.
### Nested_Allocations
When a resource allocation is included as an argument to a constructor, if the
constructor fails it can leave an an unreachable resource that no one can close.
For example gzipOutputStream = new GZIPOutputStream(new FileOutputStream(out));
is bad in case the outer constructor, GZIPOutputStream, throws an exception. In
that case, no one will have a hold of the FileOutputStream and so no one will be
able to close it.
In such a case you need to move the allocation the FileOutputStream out of the
nested position and name it, so you are able to close if anything goes wrong
during execution of the GZIPOutputStream constructor.
Here are resources that can throw exceptions i their constructor(s).
- ObjectInputStream , ObjectOutputStream, PipedInputStream, PipedOutputStream,
PipedReader, PipedWriter, JarInputStream, JarOutputStream, GZIPInputStream,
GZIPOutputStream , ZipFile all throw IOException
- PrintStream throws UnsupportedEncodingException
The constructors for FileInputStream, FileOutputStream and RandomAccessFile
throw FileNotFoundException, but these cases are not problematic in the sense
that their arguments are not resources and so they do not cause the nested
resource leak.
### Allocation of JSonParser and Cursor resources
Some resources are created inside libraries instead of by "new".
Cursor is an interface, the actual resources are something like SQLiteCursor.
So, every time you call a function that returns a Cursor object, there is an
allocation.
For instance, in the functions from SQLiteDatabase query(…) and rawQuery(…)
allocate a cursor resource. For SQLiteQueryBuilder, ContentProviderClient,
ContentResolver. MediaStore and DownloadManager it is only query(…) Cursor
objects cursor created by these functions need to be closed (i.e.,
cursor.close()).
Similarly, JsonParser is an abstract class, and create a resource in functions
from the class JsonFactory createParser(byte[] data) createParser(byte[] data,
int offset, int len) createParser(String content) createParser(URL url)
createParser(File f) JsonParser objects js created by these functions need to be
closed (jp.close()). On the other hand . JasonParsers gotten from
createParser(InputStream in) and createParser(Reader r) give you JsonParsers
that dont need to be closed. This is because they receive the resource from
somewhere that will maintain the responsibility to close it.
### Escaping resources and exceptions
Sometimes you want to return a resource to the outside, in which case you should
not close it, but you still need to be careful of exceptions in case control
skips past the return leaving no one to close. Here is a simple example of a
positive use of escaping resources.
```java
// An escaping resource, shouldn't close
public BugReportAttachment createAttachment(File reportDirectory, String fileName)
throws FileNotFoundException {
File file = new File(reportDirectory, fileName);
OutputStream stream = new FileOutputStream(file);
return new BugReportAttachment(Uri.fromFile(file), stream);
}
```
In this case it is intended that an object that wraps `stream` is passed to the
caller of `createAttachment`. You should certainly not close stream here,
because it is being passed to the outside.
But for escaping resources like this you still need to be careful of exceptions.
For example, in
```java
// An escaping resource, and a leak
public BugReportAttachment createAttachment(File reportDirectory, String fileName)
throws FileNotFoundException {
File file = new File(reportDirectory, fileName);
OutputStream stream = new FileOutputStream(file);
stream.write(7);
return new BugReportAttachment(Uri.fromFile(file), stream);
}
```
if stream.write(7) throws an exception, then no one will have a hold of stream,
and no one will be able to close it; a leak.
### Java 7's try-with-resources
**(For use with Java 7 only)**
Clearly, accounting for the ramifications of all the exceptional cases is
complicated, and there is a better way in Java 7.
```java
// Two Resources Fix 2; via try-with-resources
public static void foo() throws IOException {
try (
FileInputStream fis = new FileInputStream(new File("whatever.txt"));
FileOutputStream fos = new FileOutputStream(new File("everwhat.txt"))
) {
fos.write(fis.read());
}
}
```
All the complicated exceptional cases above are (apparently) covered by this
construct, and the result is much simpler.
So, if you are trying to fix a potential leak in code with multiples resources
you can go ahead and try to understand whether the potential leak is real. Or,
if the code is complex and it is hard to figure out, it would be perfectly
legitimate to simply convert the code over to try-with-resources if you have
access to Java 7, so as to save yourself some brain-cycles. You will also end up
with cleaner code.
If try-with-resources is so great you should <i>always</i> use it. But you
shouldn't… Try-with-resources gives resources static scoping, and works via a
stack discipline. Sometimes, you want a resource to persist beyond scope, as in
the escaping example above. In an escaping example maybe you could refactor lots
of code so that try-with-resources applies, and maybe you cannot in a sensible
way. This just illustrates that, though you might hear people say that
try-with-resources "solves" the resource problem, it does not. It is very
useful, but you cannot use it blindly when you see a resource-allocation site.