文件描述符泄漏示例?

Vik*_*ngh 9 java android memory-leaks file-descriptor memory-leak-detector

在Android中是否有任何好的示例来演示文件描述符泄漏?我读到某个地方,如果我们不关闭流,例如FileInputStream,FileOutputStream但我找不到任何好的参考示例来证明它.

请分享一些博客/代码段.谢谢!

Ran*_*niz 8

因为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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

由于延迟加载,我们将InputStream保存在PropertiesFile中,每当我们创建一个新版本时都会保留它,因为我们从不关闭流,所以我们将在这里泄漏文件描述符.

现在,当程序终止时,操作系统将关闭这些打开的文件描述符,但只要程序运行,它将继续泄漏文件描述符,如使用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
Run Code Online (Sandbox Code Playgroud)

如果我们强制GC运行,我们可以看到大多数返回:

$ jcmd 6938 GC.run
6938:
Command executed successfully
$ lsof | grep pf.properties | wc -l
2
Run Code Online (Sandbox Code Playgroud)

其余两个描述符是存储在Revision s 中的描述符.

我在我的Ubuntu机器上运行它,但如果在Android上运行,输出看起来会相似.