- PR -

メモリが解放されるのはいつ?

投稿者投稿内容
ねま
会議室デビュー日: 2004/11/10
投稿数: 18
投稿日時: 2005-07-25 20:41
環境:
cpu = Intel Celeron 2.0GHz
memory = 1GB
windows2000 sp4
jdk 1.4.2_04-b05
eclipse3.0


下記のテストプログラムを eclipse 上で動かしてみてください。
私の環境では各break point時のメモリ使用量はコード中のコメントのようになりました。

コード:

public class Test {

public static void main(String[] args) {
System.out.println("-- start --");
// break point(1) //この時タスクマネージャのメモリ使用量=6.9MB
foo();
// break point(5) // この時タスクマネージャのメモリ使用量=23.5MB
System.out.println("-- end --");
}

public static void foo() {
String str = DATA;
int count = 16; // ←18 にすると私の環境では OutOfMemoryError.

for(int i=0; i<count; i++) {
str = str + str;
System.out.println("i=" + i);
}
// break point(2)
// この時タスクマネージャのメモリ使用量=23.4MB
// メモリの増加量は 23.4 - 6.9 = 16.5MB
// そのうち str には、62byte * 2 * 16 = 4,063,232 = 4MB が格納されている
str = null;
// break point(3)
// この時タスクマネージャのメモリ使用量=23.4MB
// str=null とすることで時間が経てば(gcによって)メモリが解放されることを期待している。
System.gc();
// break point(4)
// この時タスクマネージャのメモリ使用量=23.5MB
// 明示的に gc することによってメモリが解放されることを期待している。
}

private static String DATA = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 62byte
}



そこで質問なのですが、
break point(4) においてメモリが解放されないのはなぜでしょうか?

break point(3) から (4) の間でタスクマネージャのメモリ使用量に
変化がありませんでした。

gc によって str のメモリが解放されることを期待していましたが
そうならないので困っています。

--
補足です。
もう少し経過を説明しますと、今作成中のアプリケーションにおいてメモリが
開放されない問題が起きており、そのうえさらに別の領域のメモリを確保しようとして OutOfMemoryError が発生しているような状況です。

(ちょうど上の foo() を何度も繰り返し呼んでいるうちに OutOfMemory に
 なってしまっているのです。)

で、調査のため上のテストプログラムを作成しました。

str = null; とすることで時間がたてば gc によってメモリが開放されるだろうと
思っていたのですが、一向に開放されません。
結果、System.gc() と追加してもメモリが開放されないことに気づいた次第です。

[ メッセージ編集済み 編集者: ねま 編集日時 2005-07-25 20:50 ]

[ メッセージ編集済み 編集者: ねま 編集日時 2005-07-25 20:57 ]

[ メッセージ編集済み 編集者: ねま 編集日時 2005-07-25 21:06 ]
スフレ
ぬし
会議室デビュー日: 2005/05/27
投稿数: 281
お住まい・勤務地: 東京
投稿日時: 2005-07-25 21:02
ねまさんが使っているJVMが、メモリが不必要になっても「OSに返却」するように実装されていないんでしょう。Javaのヒープ使用量(Runtime.totalMemory() - Runtime.freeMemory())は、ねまさんが期待するような動作になると思いますよ。

他のメーカーのJVMだと、別の挙動になるかもしれません。
ねま
会議室デビュー日: 2004/11/10
投稿数: 18
投稿日時: 2005-07-25 21:16
スフレさん、早速のご回答ありがとうございます。

使用しているJDKは Sun のものです。一番オーソドックスなものだと思いますけど。。。

Runtime.totalMemory() や Runtime.freeMemory() の存在は恥ずかしながら
初めてしりました。あと、Runtime.gc() なんてのもありますね。

最終的にやりたいことは、メモリリークをなくすことですので、
もう少し情報を待とうと思います。
ねま
会議室デビュー日: 2004/11/10
投稿数: 18
投稿日時: 2005-07-25 22:04
自己レスです。
引用:

(ちょうど上の foo() を何度も繰り返し呼んでいるうちに OutOfMemory に
 なってしまっているのです。)



って書きましたけど、テストプログラムのMain() の中で foo() を何回呼ぼうと
メモリリークはしませんね。初回に増えて以降メモリ使用量は 23.5MBあたりで変動なしです。
(当たり前?)

私のアプリで foo()にあたるメソッドを繰り返し実行していて OutOfMemoryError に
なるのは、やはり foo()内に問題があるんですよね。

メモリが開放されないのと、メモリリークとは関係ないですよね?
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2005-07-25 22:38
オブジェクトのリークとメモリリークを混同されていませんか?
オブジェクトを生成し続けて、参照しっぱなしならオブジェクトがリークして OutOfMemoryError になります。(これをメモリリークと呼ぶことももちろんありますが・・・・)
必要以上にオブジェクトを参照しているのでなければヒープ領域を増やしましょう。-Xmx オプションです。

JNI で malloc していたり、JITコンパイラが生成するネイティブのコードが適切に解放されず増え続ければメモリリークとなり、プロセスサイズの増加につながります。最終的に発生するのは前者と同じ OutOfMemoryErrorなのでちょっと難しいのですが。
今回問題にされているのは前者のオブジェクトのリークだと思いますのでプロセスのサイズを見てもあまり意味はありません。
こちら↓のドキュメントが参考になるかと思います。ご一読して頂くことをお勧めします。
・BEA サポートパターン > メモリ不足とメモリ リークに関する問題の調査
http://www.beasys.co.jp/cs/support_news/product_troubleshooting/Investigating_Out_of_Memory_Memory_Leak_Pattern.html

[ メッセージ編集済み 編集者: インギ 編集日時 2005-07-25 22:41 ]
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2005-07-26 02:04
もう散々意見が出てしまった後ですが・・・

ねまさんがメモリリークと仰っていますが、
23.5MBというのはJVMが明示的に確保しているメモリです。
メモリリークとは、確保した領域へのポインタへの参照方法を失っている状態です。

スフレさんが仰るように、GCによる[Javaの]メモリの解放と
[OSに対する]JVMのメモリの解放(ヒープ領域の縮小)が同期するのは
ベンダーの実装次第です。
パフォーマンスにかかわる部分になりますので、
用途に合わせたベンダー独自の実装方法が行われています。

GCの度にヒープの縮小が行われると、一般的にパフォーマンスの問題が発生します。
ヒープの確保はパフォーマンスに影響を与えます。
ヒープの拡大が行われると言う事は、GCにJVM上のメモリが解放されても
そのプログラムによってそれだけのメモリが再度必要になる可能性が高いと言う事です。
ですので、無理にヒープの縮小を行わないように作られている事が多いみたいですね。
ねま
会議室デビュー日: 2004/11/10
投稿数: 18
投稿日時: 2005-07-26 14:03
オブジェクトのリークとメモリのリークを混同していました。

メモリリークとは、ある領域のメモリが再利用できない状態のこと
ということでよろしいでしょうか?
(JVM にまかせとけばこっちの心配はいらない?)

で、Java では確保したメモリをOSに返却しないけど、
確保した容量内で、JVM が new して実際に使用したり gcで開放したり
してるってことですね?
(再利用してるからリークとは言わない)

スフレさんに教わった Runtime.totalMemory(), Runtime.freeMemory()
と、System.gc() を使ってトレースしてみると確かに
gc の前後で、タスクマネージャのメモリ使用量に変化はありませんでしたが、
heep の使用量には差があることがわかりました。(freeMemory が増えてました)


あと、基本的な質問なのですが、
new したときに、-Xmx で指定したサイズを超えてしまうと
即座に OutOfMemory になるという理解でよろしいでしょうか?
Anthyhime
ぬし
会議室デビュー日: 2002/09/10
投稿数: 437
投稿日時: 2005-07-26 16:41
> あと、基本的な質問なのですが、
> new したときに、-Xmx で指定したサイズを超えてしまうと
> 即座に OutOfMemory になるという理解でよろしいでしょうか?

まずFULL GCされます。
そのGCの結果ヒープが確保できないときにOutOfMemoryErrorで落ちるはずです。

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