有没有很好的例子演示Android中的文件描述符泄漏?我读过某个地方,如果不关闭例如流,FileInputStream或者FileOutputStream我找不到任何能证明这一点的参考实例,就会发生这种情况。
FileInputStream
FileOutputStream
请分享一些博客/代码段。谢谢!
由于Dalvik的 FileInputStream 会在被垃圾回收时自行关闭(对于OpenJDK / Oracle也是这样),因此它不如实际泄漏文件描述符那样普遍。当然,文件描述符将被“泄漏”,直到GC运行,因此取决于您的程序,可能需要一段时间才能收回它们。
为了实现更持久的泄漏,您必须通过在内存中的某个位置对其进行引用来防止对该流进行垃圾收集。
这是一个简短的示例,该示例每1秒加载一次属性文件,并跟踪每次更改的情况:
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); } } }
由于延迟加载,我们将 InputStream 保留在 PropertiesFile中 ,每当我们创建新的 Revision 时都会将其保留,并且由于我们从不关闭流,因此此处将泄漏文件描述符。
现在,当程序终止时,操作系统将关闭这些打开的文件描述符,但是只要程序正在运行,它将继续泄漏文件描述符,如使用 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
而且,如果我们强制运行GC,则可以看到大部分返回:
$ jcmd 6938 GC.run 6938: Command executed successfully $ lsof | grep pf.properties | wc -l 2
剩下的两个描述符是存储在 Revision中 的描述符。
我在Ubuntu机器上运行了此程序,但是如果在Android上运行,则输出看起来类似。