14

Android でのファイル記述子のリークを示す良い例はありますか? たとえば、ストリームを閉じないと発生することをどこかで読みましたが、それFileInputStreamFileOutputStream示す良い参考例が見つかりませんでした。

いくつかのブログ/コード スニペットを共有してください。ありがとう!

4

3 に答える 3

14

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);
        }
    }
}

遅延読み込みのため、InputStreamPropertiesFileに保持します。これは、新しいリビジョンを作成するたびに保持されます。ストリームを閉じないため、ここでファイル記述子がリークします。

現在、これらの開いているファイル記述子は、プログラムの終了時に OS によって閉じられますが、プログラムが実行されている限り、 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

残りの 2 つの記述子は、Revisionに格納されているものです。

これを Ubuntu マシンで実行しましたが、Android で実行した場合の出力は同様になります。

于 2015-08-14T07:27:41.797 に答える
-1
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

  } 
于 2015-08-14T06:02:03.347 に答える