- PR -

FileInputStreamのcloseし忘れは、メモリーリーク?

1
投稿者投稿内容
tomomo
会議室デビュー日: 2003/06/05
投稿数: 7
お住まい・勤務地: 長野
投稿日時: 2004-11-26 22:28
いつもお世話になります。tomomoと言います。
初めて投稿します。よろしくお願いします。

今、次のようなコードを書きました。

1 import java.io.*;
2 import java.util.Properties;
3
4 public class FileInputSreamNotCloseTest {
5
6 private Properties property;
7
8 public FileInputSreamNotCloseTest() {}
9
10 public void createPropertiesFile(File f)
11 throws IOException,FileNotFoundException
12 {
13 if ( f.exists()==false ) f.createNewFile();
14 this.property = new Properties();
15 this.property.load(new FileInputStream(f));
16 }
17
18 public static void main(String[] args) {
19 try {
20 for(int i=0; i<100; i++){
21 File file = new File("C:\\temp\\test.txt");
22 if( file.exists() ) System.out.println(i+"回目のstart:" + file.delete());
23
24 FileInputSreamNotCloseTest f = new FileInputSreamNotCloseTest();
25 f.createPropertiesFile(file);
26
27 if( file.exists() ) System.out.println(i+"回目のend:" + file.delete());
28 }
29 } catch (Exception e) {
30 e.printStackTrace();
31 }
32 }
33 }
34

このコードを実行すると、22行目と27行目のところで、ファイルの削
除がされない事実がわかりました。
何故か調査したところ、15行目のnew FileInputStream(f)で生成した
FileInputStreamオブジェクトをcloseしていないため、ずーと開いた
状態?になっていたので、delete()されない事がわかりました。

15行目を

FileInputStream fis = new FileInputStream(f);
this.property.load(fis);
fis.close();

これに置き換えたら、ファイルが削除されるようになりました。

前置きが長かったですが、ここからが本題なのです。

上記のようなコードの状態(FileInputStream#close()を実行しない状
態)の時ですが、new したFileInputStreamオブジェクトは、いつかGC
の対象になるものなのでしょうか?

それとも、メモリリークになり、いつかプロセスがダウンしてしまうの
でしょうか?

closeはしないといけないとは思いますが、closeをし忘れた場合、java
VMとして、どうなるのかが知りたいです。

すみませんが、よろしくお願いします。


[ メッセージ編集済み 編集者: tomomo 編集日時 2004-11-26 22:29 ]

[ メッセージ編集済み 編集者: tomomo 編集日時 2004-11-26 22:29 ]
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2004-11-26 23:22
unibon です。こんにちわ。

引用:

tomomoさんの書き込み (2004-11-26 22:28) より:
上記のようなコードの状態(FileInputStream#close()を実行しない状
態)の時ですが、new したFileInputStreamオブジェクトは、いつかGC
の対象になるものなのでしょうか?


はい、"いつか"はなります(なにかメモリーを消費するコードを追加すると、"いつか" GC が起きて FileInputStream のインスタンスが解放されることが分かります)。
また、
http://java.sun.com/j2se/1.4/ja/docs/ja/api/java/io/FileInputStream.html#finalize()
の仕様により、FileInputStream クラスの finalize メソッドの中では close されることが保証されています。
これらの2つのことから、長期的にはメモリーリークにはならないと結論できます。
ただし、短期的(GC が動くまでの間)はメモリーリークです。ファイルハンドルのリソースの分だけメモリーが無駄に使われています。したがって、finalize での close に頼ることは、やっぱり避けるべきです。
tomomo
会議室デビュー日: 2003/06/05
投稿数: 7
お住まい・勤務地: 長野
投稿日時: 2004-11-29 10:24
unibonさん。こんにちは。

勉強になりました。
使ったものは後片付けする事を忘れないように
心がけます。ありがとうございました。
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2004-11-29 10:31
ガベージコレクションの対象となっているものを「メモリリーク」と呼ぶかどうかは微妙(っていうか正確には違う)ですが、close()するべきですね。
close()漏れがあるとファイルハンドラ(Windows)やファイルデスクリプタ(Unix)が枯渇して新規にファイルをオープンできなくなったりします。
大抵 too many open files とかいうメッセージと共に IOException が発生します。
・BEA Troubleshooting Guidance > Support Diagnostic Patterns > Too Many Open Files
http://support.bea.com/support_news/product_troubleshooting/Too_Many_Open_Files_Pattern.html
Wata
ぬし
会議室デビュー日: 2003/05/17
投稿数: 279
投稿日時: 2004-11-29 14:44
こんにちは、Wataです。
引用:

tomomoさんの書き込み (2004-11-26 22:28) より:
15行目を

FileInputStream fis = new FileInputStream(f);
this.property.load(fis);
fis.close();

これに置き換えたら、ファイルが削除されるようになりました。


について、
ストリームのクローズはfinallyブロックを使って行うようにしてください。
コード:
FileInputStream fis = new FileInputStream(f);
try{
    this.property.load(fis);
} finally {
    fis.close();
}


こうしておけば、loadメソッドがIOExceptionを投げた場合でも確実にclose()を呼び出せます。
基本イディオムとして、覚えておくといいですよ。
1

スキルアップ/キャリアアップ(JOB@IT)