文件描述符泄漏示例?
File descriptor leak example?
在 Android 中是否有任何好的示例可以证明文件描述符泄漏?我在某处读到,如果我们不关闭流,例如 FileInputStream
或 FileOutputStream
,它就会发生,但我找不到任何好的参考示例来证明它。
请分享一些 blog/code 片段。谢谢!
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);
}
}
}
想法是关闭 finally
块中的文件描述符。无论是成功完成还是发生异常,文件描述符都会被正确关闭。
现在,如果您正在寻找一些东西来演示如何不正确地执行此操作,只需将此代码包装在 while(1)
循环中,注释掉 in.close()
行,然后将 break;
在你的 catch 块中,这样当它爆炸时你就会跳出你的无限循环。
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
}
因为 Dalvik 的 FileInputStream 会 close itself when it is garbage collected(OpenJDK/Oracle 也是如此)所以实际泄漏文件描述符并不像您想象的那么常见.当然,在 GC 运行 之前,文件描述符将是 "leaked",因此根据您的程序,它们可能需要一段时间才能被回收。
要实现更持久的泄漏,您必须通过在内存中的某处保留对流的引用来防止流被垃圾收集。
这是一个每 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 关闭,但只要程序处于 运行ning 状态,它就会继续泄漏文件描述符,可以看出通过使用 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
其余两个描述符是存储在修订中的描述符。
我在我的 Ubuntu 机器上 运行 这个,但是如果 运行 在 Android.
上,输出看起来会相似
在 Android 中是否有任何好的示例可以证明文件描述符泄漏?我在某处读到,如果我们不关闭流,例如 FileInputStream
或 FileOutputStream
,它就会发生,但我找不到任何好的参考示例来证明它。
请分享一些 blog/code 片段。谢谢!
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);
}
}
}
想法是关闭 finally
块中的文件描述符。无论是成功完成还是发生异常,文件描述符都会被正确关闭。
现在,如果您正在寻找一些东西来演示如何不正确地执行此操作,只需将此代码包装在 while(1)
循环中,注释掉 in.close()
行,然后将 break;
在你的 catch 块中,这样当它爆炸时你就会跳出你的无限循环。
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 }
因为 Dalvik 的 FileInputStream 会 close itself when it is garbage collected(OpenJDK/Oracle 也是如此)所以实际泄漏文件描述符并不像您想象的那么常见.当然,在 GC 运行 之前,文件描述符将是 "leaked",因此根据您的程序,它们可能需要一段时间才能被回收。
要实现更持久的泄漏,您必须通过在内存中的某处保留对流的引用来防止流被垃圾收集。
这是一个每 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 关闭,但只要程序处于 运行ning 状态,它就会继续泄漏文件描述符,可以看出通过使用 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
其余两个描述符是存储在修订中的描述符。
我在我的 Ubuntu 机器上 运行 这个,但是如果 运行 在 Android.
上,输出看起来会相似