eclipse 在 callable.call 中没有关闭的 BufferedReader 会发生什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12203291/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 18:53:53  来源:igfitidea点击:

What happens to a BufferedReader that doesn't get closed within a callable.call?

javaeclipseinner-classesbufferedreadercallable

提问by GLaDOS

I have three questions.

我有三个问题。

To explain, I was reviewing someone's code, and noticed BufferedReaders sometimes aren't being closed. Usually, Eclipse gives a warning that this is a potential memory leak (and I fix it). However, within a Callable inner class, there is no warning.

解释一下,我正在某人的代码,并注意到BufferedReaders 有时没有被关闭。通常,Eclipse 会发出警告,指出这是潜在的内存泄漏(我已修复它)。但是,在 Callable 内部类中,没有警告。

class outerClass {
    ...
    public void someMethod() {
        Future<Integer> future = outputThreadPool.submit(new innerClass(this.myProcess.getInputStream(), threadName));
        ...
    }

    class innerClass implements Callable<Integer> {
        private final InputStream stream;
        private final String prepend;

        innerClass(InputStream stream, String prepend) {
            this.stream = stream;
            this.prepend = prepend;
        }

        @Override
        public Integer call() {
            BufferedReader stdOut = new BufferedReader(new InputStreamReader(stream));
            String output = null;
            try {
                while ((output = stdOut.readLine()) != null) {
                    log.info("[" + prepend + "] " + output);
                }

            } catch (IOException ignore) {
            // I have no idea why we're ignoring this... :-|        
            }
            return 0;   
        }
    }
}

The people who wrote the code are experienced Java developers, so my first thought is that it's intentional... but it could be they were in a hurry when they wrote it and just overlooked it.

编写代码的人都是有经验的 Java 开发人员,所以我的第一个想法是这是故意的……但也可能是他们编写代码时匆忙而忽略了它。

My questions are:

我的问题是:

  1. Why does Eclipse not highlight this (which may be answered by the answer to the following questions)?

  2. What is the worst that could happen if it's closed within the call() method? (I can't think of a good reason... and I've been searching for a while... but maybe it was intentional not to close the BufferedReader)

  3. What is the worst that could happen if the BufferedReader is notclosed within the inner class?

  1. 为什么 Eclipse 没有突出显示这一点(可以通过以下问题的答案来回答)?

  2. 如果它在 call() 方法中关闭,可能发生的最坏情况是什么?(我想不出一个很好的理由......我已经搜索了一段时间......但也许是故意不关闭 BufferedReader)

  3. 如果 BufferedReader没有在内部类中关闭,最糟糕的情况是什么?

采纳答案by Brian

I would say that since they're creating a BufferedReaderaround a given InputStream, the code is safe not calling close(). The code that calls close()should always be the code that creates the stream and done using try/finally.

我会说,因为他们BufferedReader围绕给定InputStreamclose(). 调用的代码close()应该始终是创建流并使用 try/finally 完成的代码。

public static void read(String str) throws IOException {
    FileInputStream stream = null
    try {
        stream = new FileInputStream(str);
        readStreamToConsole(stream);
    } finally {
        if (stream != null)
            stream.close();
    }
}

private static void readStreamToConsole(InputStream stream) {
    BufferedReader stdOut = new BufferedReader(new InputStreamReader(stream));
    String output = null;
    while ((output = stdOut.readLine()) != null)
        System.out.println(output);
}

Another note: your code appears to be logging output from some other process. You probably wouldn't be able to close the stream anyway. Without testing it myself, I'm not sure what would happen if you closed a stream from another process.

另一个注意事项:您的代码似乎正在记录来自其他某个进程的输出。无论如何,您可能无法关闭流。如果没有自己测试,我不确定如果您关闭来自另一个进程的流会发生什么。

Oh, and the IOExceptionisn't likely to happen because the stream is coming from another process. That's not likely to happen unless some irrecoverable error occurs. It still wouldn't be a bad idea to log the exception somehow, though.

哦,这IOException不太可能发生,因为流来自另一个进程。除非发生一些不可恢复的错误,否则这不太可能发生。不过,以某种方式记录异常仍然不是一个坏主意。



Edit to address your comment about mixed answers:

编辑以解决您对混合答案的评论:

Let's use an output stream and BufferedWriteras an example this time:

让我们使用输出流BufferedWriter作为例子:

private static final String NEWLINE = System.getProperty("line.separator");

public static void main(String[] args) throws IOException {
    String file = "foo/bar.txt";
    FileOutputStream stream = null;
    try {
        stream = new FileOutputStream(file);
        writeLine(stream, "Line 1");
        writeLine(stream, "Line 2");
    } finally {
        if (stream != null)
            stream.close();
    }
}

private static void writeLine(OutputStream stream, String line) throws IOException {
    BufferedWriter writer = new BufferedWriter(new InputStreamWriter(stream));
    writer.write(line + NEWLINE);
}

This works. The writeLine method is used as a delegate to creating writerand actually writing a single lineto the file. Of course, this logic could be something more complex, such as turning an object into a Stringand writing it. This makes the mainmethod a little easier to read too.

这有效。writeLine 方法用作创建writer单个line文件并将其实际写入文件的委托。当然,这个逻辑可以更复杂一些,比如把一个对象变成 aString并写出来。这也使该main方法更易于阅读。

Now, what if instead, we closed the BufferedWriter?

现在,如果我们关闭 BufferedWriter 呢?

private static void writeLine(OutputStream stream, String line) throws IOException {
    BufferedWriter writer = null;
    try {
        writer = new BufferedWriter(new InputStreamWriter(stream));
        writer.write(line + NEWLINE);
    } finally {
        if (writer != null)
            writer.close();
    }
}

Try running it with that, and it will fail every time on the second writeLinecall. It's good practice to always close streams where they're created, instead of where they're passed. It may be fine initially, but then trying to change that code later could result in errors. If I started with only 1 writeLinecall with the bad method and someone else wanted to add a second one, they'd have to refactor the code so that writeLinedidn't close the stream anyway. Getting close-happy can cause headaches down the road.

尝试运行它,它会在第二次writeLine调用时失败。总是在创建流的地方而不是通过它们的地方关闭流是一种很好的做法。最初可能没问题,但稍后尝试更改该代码可能会导致错误。如果我开始时只writeLine调用了1 次错误方法,而其他人想添加第二次调用,则他们必须重构代码,以便writeLine无论如何都不会关闭流。获得亲密的快乐可能会导致头痛。

Also note that technically, the BufferedWriterisn't the actual handle to your system resource, the FileOutputStreamis, so you should be closing on the actual resource anyway.

另请注意,从技术上讲,这BufferedWriter不是系统资源的实际句柄,FileOutputStream而是,因此无论如何您都应该关闭实际资源。

So, rule of thumb: only close your streams where you create them, and always do the creation and closing in a try/finally block (or Java 7's awesome try/resource block, which does the closing for you).

所以,经验法则:只在你创建它们的地方关闭你的流,并且总是在 try/finally 块(或 Java 7 的很棒的try/resource 块,它为你关闭)中进行创建和关闭。

回答by Kenster

You may not want to close the BufferedReaderin this case. The InputStreamwhich was passed to the constructor is the object that may be associated with a system resource. The BufferedReaderand the InputStreamReaderare just wrappers around that. Closing the BufferedReaderwould also close the InputStream, which may not be what the caller wanted.

BufferedReader在这种情况下,您可能不想关闭。在InputStream将其传递给构造是可与系统资源相关联的对象。TheBufferedReader和 theInputStreamReader只是围绕它的包装。关闭BufferedReader也会关闭InputStream,这可能不是调用者想要的。

回答by Roman C

  1. Doesn't highlight and it's true, because a stream can be closed somewhere else out of the call method.

  2. If it's closed within the call method that other threads may be using it at the moment.

  3. With the BufferdRreadernothing but if you loose the reference to stream and cannot close it and this leads to a memory leak.

  1. 没有突出显示,这是真的,因为流可以在调用方法之外的其他地方关闭。

  2. 如果它在 call 方法中关闭,则其他线程目前可能正在使用它。

  3. 随着BufferdRreader不过,如果你松流的参考,不能关闭它,这会导致内存泄漏。

回答by Jiri Kremser

No matter where you open the stream you should close it in the finally block and it is a good practice to test the stream for nullas well, because if the file is not existing the stream will be null, exception will be thrown (FileNotFoundException) but the finally bock is done. I.e. the content of the call method should be:

无论您在哪里打开流,您都应该在 finally 块中关闭它,并且测试流也是一个好习惯null,因为如果文件不存在,流将是null,异常将被抛出 ( FileNotFoundException) 但 finally博克完成。即调用方法的内容应该是:

   BufferedReader stdOut = null;
   String output = null;
        try {
            stdOut = new BufferedReader(new InputStreamReader(stream));
            while ((output = stdOut.readLine()) != null) {
                log.info("[" + prepend + "] " + output);
            }
        } catch (FileNotFoundException ex) {
            log.warn("Unable to open nonexisten file " + whichOne);  
        } catch (IOException ex) {
            log.warn("Unable to read from stream");  
        } finally {
            if (stdOut != null) {
                try {
                   stdOut.close();
                } catch (IOException e) {
                    log.warn("Unable to close the stream");
                }
            }
        }
        return 0;

Or if you are allowed to use Java 7 you can use the benefits of AutoCloseableinterface and the new language structure for this purpose. see http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

或者,如果您被允许使用 Java 7,您可以AutoCloseable为此目的使用界面和新语言结构的好处。见http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

回答by kosa

BuffereddReader close()

BuffereddReader关闭()

Closes this stream and releases any system resources associated with it. If the stream is already closed then invoking this method has no effect.

关闭此流并释放与其关联的所有系统资源。如果流已经关闭,则调用此方法无效。

So, if you don't close(), system resources may be still associated with the reader which may cause memory leak.

因此,如果您不关闭(),系统资源可能仍然与阅读器相关联,这可能会导致内存泄漏。

Why eclipse not highlighting: it is not compile time error if you ignore calling close(), so eclipse don't highlight.

为什么 eclipse 不突出显示:如果忽略调用 close(),这不是编译时错误,因此 eclipse 不突出显示。