Example file descriptor descriptor example?

Is there a good example demonstrating a file descriptor leak in Android? I read somewhere that this happens if we do not close the streams, for example, FileInputStream or FileOutputStream , but I could not find a good reference example demonstrating it.

Share some blog / code snippets. thanks!

+7
java android memory-leaks file-descriptor
source share
3 answers

Since Dalvik FileInputStream will close itself when it collects garbage (this is also true for OpenJDK / Oracle), it is less common, d I think that file descriptors actually leak. Of course, file descriptors will β€œleak out” until the GC works, so depending on your program, this may take some time before they are fixed.

To perform a more permanent leak, you will need to prevent garbage collection by keeping a link to it somewhere in memory.

Here is a quick example that loads a properties file every 1 second and tracks it every time it changes:

 public class StreamLeak { /** * A revision of the properties. */ public static class Revision { final ZonedDateTime time = ZonedDateTime.now(); final PropertiesFile file; Revision(PropertiesFile file) { this.file = file; } } /* * Container for {@link Properties} that implements lazy loading. */ public static class PropertiesFile { private final InputStream stream; private Properties properties; PropertiesFile(InputStream stream) { this.stream = stream; } Properties getProperties() { if(this.properties == null) { properties = new Properties(); try { properties.load(stream); } catch(IOException e) { e.printStackTrace(); } } return properties; } @Override public boolean equals(Object o) { if(o instanceof PropertiesFile) { return ((PropertiesFile)o).getProperties().equals(getProperties()); } return false; } } public static void main(String[] args) throws IOException, InterruptedException { URL url = new URL(args[0]); LinkedList<Revision> revisions = new LinkedList<>(); // Loop indefinitely while(true) { // Load the file PropertiesFile pf = new PropertiesFile(url.openStream()); // See if the file has changed if(revisions.isEmpty() || !revisions.getLast().file.equals(pf)) { // Store the new revision revisions.add(new Revision(pf)); System.out.println(url.toString() + " has changed, total revisions: " + revisions.size()); } Thread.sleep(1000); } } } 

Due to lazy loading, we save the InputStream in a PropertiesFile, which will be stored whenever we create a new Revision, and since we never close the stream, we will skip the file descriptors here.

Now these open file descriptors will be closed by the OS when the program terminates, but while the program is running, it will continue to leak file descriptors, which can be seen from lsof :

 $ lsof | grep pf.properties | head -n 3 java 6938 raniz 48r REG 252,0 0 262694 /tmp/pf.properties java 6938 raniz 49r REG 252,0 0 262694 /tmp/pf.properties java 6938 raniz 50r REG 252,0 0 262694 /tmp/pf.properties $ lsof | grep pf.properties | wc -l 431 

And if we run GC, we will see that most of them are returned:

 $ jcmd 6938 GC.run 6938: Command executed successfully $ lsof | grep pf.properties | wc -l 2 

The other two descriptors are those that are stored in versions.

I ran this on my Ubuntu machine, but the result would be similar if you run on Android.

+6
source share
 InputStream in; try { in = new BufferedInputStream(socket.getInputStream()); // Do your stuff with the input stream } catch (Exception e) { // Handle your exception } finally { // Close the stream here if (in != null) { try { in.close(); } catch (IOException e) { Log.e(TAG, "Unable to close stream: " + e); } } } 

The idea is to close the file descriptor in the finally block. If you succeed or an exception occurs, the file descriptor will close correctly.

Now, if you are looking for something to demonstrate how NOT to do it right, just wrap this code in a while(1) , comment out the line in.close() and put break; into your catch block so that when it explodes you will break out of your infinite loop.

0
source share
 InputStream in; try { in = new FileInputStream(new File("abc"); in.read(); // Do some stuff with open fileinputstream // If an exception is generated, inputstream object will not be closed // as the next statement will not be executed, instead jumping to // the catch block. this will cause a leak of the fd assigned to file // "abc" while opening it in.close()' } catch (Exception e) { // Handle your exception } 
-one
source share

All Articles