2012-08-30 7 views
8

私には3つの質問があります。Callable.call内で閉じられないBufferedReaderはどうなりますか?

説明すると、私は誰かのコードを見直していて、BufferedReaderが閉じられていないことがあることに気づいた。通常、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; 
     } 
    } 
} 

コードを書いた人は、Java開発者を経験しているので、私の最初に考えたのは、それが意図的であるということです...しかし、彼らはそれを書き、それを見落としたときに、それは彼らが急いでいたことができました。

私の質問は以下のとおりです。

  1. なぜEclipseは(次の質問への答えによって回答されてもよい)、これをハイライト表示されませんか?

  2. call()メソッド内で閉じられた場合に起こりうる最悪のことは何ですか? (私は正当な理由は考えられません...しばらく探していましたが、BufferedReaderを閉じないように意図的に考えていたかもしれません)

  3. BufferedReaderはではないは内部クラス内で閉じられていますか?

+1

はない警告にフラグを立てることは、より私は互いに矛盾するように見えるカップルの答えを持っている –

+0

「それはこの場合には、それを行うには大丈夫です」と言うために意識的な決定よりも、Eclipseのバグのように聞こえます。一方で、BufferedReaderを閉じなければならないという人もいます。一方、彼らはそれを閉じても、それを閉じてInputStreamを閉じてしまうので、呼び出すクラスはそれを必要とするかもしれないと言います... – GLaDOS

+1

[この質問を見る](http://stackoverflow.com/questions/) 1388602/do-i-need-to-close-both-filereader-and-bufferedreader)を参照してください。 'InputStream'のようなリソースがメソッドに渡されている場合、他の誰かがそれを開いたことを意味します。他の誰かがそれを閉じなければなりません。彼らがあなたのメソッドにストリームを渡した後に、別の場所でそれを使用しようとすると、あなたのメソッドが(バッファの読み込みを中断して)ストリーム。 – Brian

答えて

6

の周りにInputStreamを作成しているので、コードはclose()を呼び出すのが安全ではないと言います。 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); 
} 

もう1つの注意:あなたのコードは、他のプロセスからの出力を記録しているようです。とにかくストリームを閉じることはできません。自分でテストすることなく、別のプロセスからストリームを閉じた場合にどうなるかわかりません。

ああ、ストリームが別のプロセスから来ているので、IOExceptionは起こりそうにありません。回復不能なエラーが発生しない限り、これは起こりそうにありません。しかし、何らかの理由で例外を記録するのは悪い考えではありません。


編集混合回答についてあなたのコメントに対処するために:のは、一例として、この時間は、出力ストリームと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); 
} 

これは動作します。 writeLineメソッドは、writerを作成し、実際にはlineというファイルをファイルに書き込む代理人として使用されます。もちろん、このロジックは、オブジェクトをに変換して書き込むなど、もっと複雑なものになる可能性があります。これにより、mainメソッドも少し読みやすくなります。

代わりに、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(); 
    } 
} 

はそれでそれを実行してみてください、そして、それは二writeLine呼び出しで毎回失敗します。渡された場所ではなく、作成された場所を常に閉じることをお勧めします。最初は問題ないかもしれませんが、後でそのコードを変更しようとするとエラーが発生する可能性があります。私が最初に悪い方法でwriteLineを呼び出すだけで、もう1人が2番目のコードを追加したい場合は、writeLineがストリームを閉じないようにコードをリファクタリングする必要があります。近くで幸せになれば、頭痛の原因になることがあります。

また、BufferedWriterはシステムリソースへの実際のハンドルではありませんので、FileOutputStreamです。したがって、とにかく実際のリソースをクローズする必要があります。

したがって、ストリームを作成した場所を閉じ、try/finallyブロック(またはJavaの7のawesome try/resource block、あなたのために閉じます)で常に作成と終了を行います。

+0

そうです...私はストリームが実際に作成された場所を見に行きました。それはそこで閉鎖されています。ありがとう! – GLaDOS

+0

また、別のプロセスからのログ出力については、このプロセスでは、問題のストリームによってストリーミングされているファイルに関する作業が行われていたため、ログ内の潜在的なエラーや問題を追跡することが重要でした。 – GLaDOS

+0

また、素晴らしい解答をいただきありがとうございます。あなたは書き込み時に問題のコードを監督していた私の開発者よりも多くの助けを借りていました:) – GLaDOS

1

BuffereddReader close()

はこのストリームを解放 、それに関連するすべてのシステムリソースを閉じます。ストリームがすでに閉じられている場合、このメソッドを呼び出すと、 の効果はありません。

したがって、close()を行わないと、システムリソースがリーダに関連付けられている可能性があり、メモリリークの原因となる可能性があります。

なぜEclipseが強調表示されないのですか?close()を呼び出すことを無視すると、コンパイル時エラーではないため、Eclipseは強調表示されません。

+0

それは私が分かっている質問3 ...に部分的に答えます。しかし、それがスレッド内にある場合は、おそらくスレッドが終了したときにガベージコレクションされますか? – GLaDOS

+0

はい、BufferedReaderオブジェクトだけGCedできますが、他のSocket関連のものは、私はGCがそれについて何もしないと思います。 – kosa

+0

しかし、eclipse _does_は、それが内部クラスにないときにハイライトします...それが私を困惑させます。 – GLaDOS

1

ストリームを開いてもfinallyブロックでストリームを閉じる必要があります。ファイルが存在しない場合はnullになるため、nullのストリームをテストすることをお勧めします。投げられた(FileNotFoundException)が、最終的にボックが行われます。私。呼び出しメソッドの内容は、次のようになります。

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; 

それとも、あなたがAutoCloseableインターフェイスと、この目的のための新しい言語構造の利点を使用できるJava 7を使用することを許可されている場合。 http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

+0

stdOutはnullになることはありません。したがって、閉じる前にifブロックにポイントがありません。構築が失敗した場合、finallyブロックに到達することはありません。 –

+0

ああ、そうです。私は少し編集しました。今、それは正しいです。 –

+0

私たちはいつでもすぐに7に行くことはありません:( – GLaDOS

1

この場合、BufferedReaderを閉じたくない場合があります。コンストラクタに渡されたInputStreamは、システムリソースに関連付けられるオブジェクトです。 BufferedReaderInputStreamReaderはそのまわりのラッパーです。 BufferedReaderを閉じると、InputStreamも閉じますが、これは発信者が望んでいない可能性があります。

1
  1. 強調表示されていないので、ストリームは呼び出しメソッドのどこかで閉じることができます。

  2. 呼び出しメソッド内で閉じられていて、他のスレッドが現在使用している可能性がある場合。

  3. BufferdRreaderでは、ストリームへの参照が緩んでいて、それを閉じることができず、メモリリークが発生します。

+0

+1簡潔に – GLaDOS

関連する問題