Android でのファイル記述子のリークを示す良い例はありますか? たとえば、ストリームを閉じないと発生することをどこかで読みましたが、それFileInputStream
をFileOutputStream
示す良い参考例が見つかりませんでした。
いくつかのブログ/コード スニペットを共有してください。ありがとう!
Android でのファイル記述子のリークを示す良い例はありますか? たとえば、ストリームを閉じないと発生することをどこかで読みましたが、それ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に保持します。これは、新しいリビジョンを作成するたびに保持されます。ストリームを閉じないため、ここでファイル記述子がリークします。
現在、これらの開いているファイル記述子は、プログラムの終了時に 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 で実行した場合の出力は同様になります。
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 }