java 文件描述符泄漏示例?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31237918/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
File descriptor leak example?
提问by Vikasdeep Singh
Is there any good example do demonstrate file descriptor leak in Android? I read somewhere that it occurs if we don't close the streams for example FileInputStream
or FileOutputStream
but I could not find any good reference example which demonstrates it.
有什么好的例子可以证明Android中的文件描述符泄漏吗?我在某处读到如果我们不关闭流就会发生这种情况,FileInputStream
或者FileOutputStream
我找不到任何好的参考示例来演示它。
Please share some blog/code snippet. thank you!
请分享一些博客/代码片段。谢谢!
采纳答案by Raniz
Because Dalvik's FileInputStreamwill close itself when it is garbage collected(this is also true for OpenJDK/Oracle) it is less common than you'd think to actually leak file descriptors. Of course, the file descriptors will be "leaked" until the GC runs so depending on your program it could take a while before they are reclaimed.
因为 Dalvik 的FileInputStream会在被垃圾收集时自行关闭(这对于 OpenJDK/Oracle 也是如此),所以实际上泄漏文件描述符的情况比您想象的要少。当然,文件描述符将被“泄漏”,直到 GC 运行,因此根据您的程序,在回收它们之前可能需要一段时间。
To accomplish a more permanent leak you will have to prevent the stream from being garbage collected by keeping a reference to it somewhere in memory.
为了实现更持久的泄漏,您必须通过在内存中的某处保留对流的引用来防止流被垃圾收集。
Here's a short example that loads a properties file every 1 second and keeps track of every time it has changed:
这是一个简短的示例,它每 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);
}
}
}
Because of the lazy loading we keep the InputStreamin the PropertiesFilewhich will be kept whenever we create a new Revisionand since we never close the stream we will be leaking file descriptors here.
由于延迟加载,我们将InputStream保存在PropertiesFile 中,每当我们创建新的修订版时都会保留它,并且由于我们从不关闭流,我们将在这里泄漏文件描述符。
Now, these open file descriptors will be closed by the OS when the program terminates, but as long as the program is running it will continue to leak file descriptors as can be seen by using lsof:
现在,当程序终止时,操作系统将关闭这些打开的文件描述符,但只要程序正在运行,它就会继续泄漏文件描述符,如使用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
And if we force the GC to run we can see that most of these are returned:
如果我们强制 GC 运行,我们可以看到其中大部分都返回了:
$ jcmd 6938 GC.run
6938:
Command executed successfully
$ lsof | grep pf.properties | wc -l
2
The remaining two descriptors are the ones stored in the Revisions.
剩下的两个描述符是存储在Revision 中的描述符。
I ran this on my Ubuntu machine but the output would look similar if run on Android.
我在我的 Ubuntu 机器上运行了这个,但如果在 Android 上运行,输出看起来很相似。
回答by Joboodi
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);
}
}
}
The idea is to close your file descriptor in the finally
block. Whether you finish successfully or an exception occurs, the file descriptor will be properly closed.
这个想法是关闭finally
块中的文件描述符。无论您成功完成还是发生异常,文件描述符都会被正确关闭。
Now, if you're looking for something to demonstrate how to NOT do this properly, just wrap this code in a while(1)
loop, comment out the in.close()
line, and put a break;
in your catch block so that when it blows up you'll break out of your infinite loop.
现在,如果您正在寻找一些东西来演示如何不正确地执行此操作,只需将此代码包装在一个while(1)
循环中,注释掉该in.close()
行,然后将 abreak;
放入您的 catch 块中,以便当它爆炸时您会跳出你的无限循环。
回答by SCCC
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 }
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 }