C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[13]: 『「GC.Collect」メソッド』について


(過去ログ 61 を表示中)

[トピック内 18 記事 (1 - 18 表示)]  << 0 >>

■35188 / inTopicNo.1)  『「GC.Collect」メソッド』について
  
□投稿者/ 尾塩 (1回)-(2009/04/21(Tue) 17:50:52)

分類:[C#] 

2月の東京勉強会で初参加させていただいた尾塩です。
初投稿ですがよろしくお願いいたします。

GCの動きに関してです。
明示的に「GC.Collect」メソッドを実行しガーベージコレクションを行うプログラムにおいて、「GC.Collect」メソッド直後に「Sleep」で一定時間ウェイトしてからメモリを確保した場合とそうでない場合とで動作が異なることがあります。
前者の場合は正常にメモリを確保できますが、後者の場合は「OutOfMemoryException」例外が発生してしまいます。

「GC.Collect」メソッドは、ファイナライズを含めて全てのガーベージコレクション処理が完了していなくても制御を戻すのでしょうか?
また完了していなくても制御を戻す場合、確実にガーベージコレクション処理が完了するまで待つ方法はないでしょうか?

このような場合のGC.Collectメソッドの動作に関して、MSの規約などから見つけられなかったのですが、どこかに説明はありますか?
教えていただきたく・・・。

引用返信 編集キー/
■35189 / inTopicNo.2)  Re[1]: 『「GC.Collect」メソッド』について
□投稿者/ やじゅ (977回)-(2009/04/21(Tue) 18:25:26)
やじゅ さんの Web サイト
No35188 (尾塩 さん) に返信

> 明示的に「GC.Collect」メソッドを実行しガーベージコレクションを行うプログラムにおいて、
> 「GC.Collect」メソッド直後に「Sleep」で一定時間ウェイトしてからメモリを確保した場合とそうでない場合とで動作が異なることがあります。
> 前者の場合は正常にメモリを確保できますが、後者の場合は「OutOfMemoryException」例外が発生してしまいます。
>

第 5 章 「マネージ コード パフォーマンスの向上」
http://msdn.microsoft.com/ja-jp/library/ms998547.aspx
によると、System.GC.Collect()を1行書くのではなく、下記3行を書く方がいいみたい。
GC.Collect()
GC.WaitForPendingFinalizers()  ←ここ
GC.Collect()

引用返信 編集キー/
■35190 / inTopicNo.3)  Re[2]: 『「GC.Collect」メソッド』について
□投稿者/ 囚人 (345回)-(2009/04/21(Tue) 18:46:43)
ガベージコレクションは専用のスレッドで動くので、GC.Collect 呼び出し直後にガベージコレクションの実行が終了しているとは限りません。

>によると、System.GC.Collect()を1行書くのではなく、下記3行を書く方がいいみたい。

ファイナライザの話だから必ずしも適切でないような…。全てのオブジェクトがファイナライザを持つわけではないので。


>前者の場合は正常にメモリを確保できますが、後者の場合は「OutOfMemoryException」例外が発生してしまいます。

でも、この状態がよろしくない気がしますね。本来、GC が必要なら実行されるわけで、わざわざ GC.Collect する必要ないはずですが…。
引用返信 編集キー/
■35191 / inTopicNo.4)  Re[3]: 『「GC.Collect」メソッド』について
□投稿者/ ぱぱいやん (1回)-(2009/04/21(Tue) 19:15:38)
ぱぱいやん さんの Web サイト
>「GC.Collect」メソッドは、ファイナライズを含めて全てのガーベージコレクション処理が完了していなくても制御を戻すのでしょうか?

囚人さんも言っているけど、別スレッドで動作するので戻します。

>また完了していなくても制御を戻す場合、確実にガーベージコレクション処理が完了するまで待つ方法はないでしょうか?

アクセスできないメモリは実行中も変化するので確実という場合はないかもしれないけど、GC.WaitForPendingFinalizers() とかでダメかなぁ。
引用返信 編集キー/
■35192 / inTopicNo.5)  Re[1]: 『「GC.Collect」メソッド』について
□投稿者/ Jitta on the way (302回)-(2009/04/21(Tue) 19:33:35)
No35188 (尾塩 さん) に返信
>
> GCの動きに関してです。
> 明示的に「GC.Collect」メソッドを実行しガーベージコレクションを行うプログラムにおいて、「GC.Collect」メソッド直後に「Sleep」で一定時間ウェイトしてからメモリを確保した場合とそうでない場合とで動作が異なることがあります。
> 前者の場合は正常にメモリを確保できますが、後者の場合は「OutOfMemoryException」例外が発生してしまいます。


sleep する時間によって変わるのでしょうか。また、GC.Collect 前後にどの様なコードがある、あるいはどの様なマネージド メモリを確保/解放しているのでしょう。
引用返信 編集キー/
■35196 / inTopicNo.6)  Re[2]: 『「GC.Collect」メソッド』について
□投稿者/ 倉田 有大 (579回)-(2009/04/21(Tue) 21:37:02)
2009/04/21(Tue) 21:37:42 編集(投稿者)

エスパー解凍すると、メモリー食ってるオブジェクトのDisposeをきちんと呼んでやるといいんじゃないかな。
Imageオブジェクトなんて、メモリー食べる食べる。
引用返信 編集キー/
■35197 / inTopicNo.7)  Re[3]: 『「GC.Collect」メソッド』について
□投稿者/ なちゃ (267回)-(2009/04/21(Tue) 23:19:33)
>>「GC.Collect」メソッドは、ファイナライズを含めて全てのガーベージコレクション処理が完了していなくても制御を戻すのでしょうか?
>囚人さんも言っているけど、別スレッドで動作するので戻します。

んーそうでしたっけ?

GCの、正確にはコレクション処理は並行に実行される可能性がありますが、その後のメモリの解放処理にあたる処理は、完全に同期的に実行されるのでは?
※GCでは世代間の移動とヒープのコンパクションが発生する(つまりオブジェクトが移動する可能性がある)ため、一時的に全スレッドが停止されたはずです。

GC.Collectは、動作的にはこの同期的な解放処理部分が完了してから帰るような気がします。

もちろんファイナライズ処理はそもそも非同期に動いているので、1回のGC.Collectだけでは完了していませんが(ファイナライズを実行できる状態になっただけ)。

引用返信 編集キー/
■35198 / inTopicNo.8)  Re[4]: 『「GC.Collect」メソッド』について
□投稿者/ なちゃ (268回)-(2009/04/21(Tue) 23:22:09)
>>>「GC.Collect」メソッドは、ファイナライズを含めて全てのガーベージコレクション処理が完了していなくても制御を戻すのでしょうか?
>>囚人さんも言っているけど、別スレッドで動作するので戻します。

> んーそうでしたっけ?

あ、すみません、これはどっちかっていうと、
>ガベージコレクションは専用のスレッドで動くので、GC.Collect 呼び出し直後にガベージコレクションの実行が終了しているとは限りません。
に対する話ですね。
紛らわしかったかもしれません。

引用返信 編集キー/
■35202 / inTopicNo.9)  Re[5]: 『「GC.Collect」メソッド』について
□投稿者/ 渋木宏明(ひどり) (1121回)-(2009/04/22(Wed) 09:14:54)
渋木宏明(ひどり) さんの Web サイト
2009/04/22(Wed) 09:34:20 編集(投稿者)

> 紛らわしかったかもしれません。

いや、もともとの

>別スレッドで動作するので戻します。

って表現が、どの範囲を説明しているのか曖昧なので、補足はしておかないと。

.NET では、GC 処理は放っておいてもバックグラウンドスレッドで適当な間隔で実行されるていわけで、仮に GC.Collect() が「GC 開始」の指示だけ行って非同期で戻ってくるのだとしたら、そんなメソッドを明示的に呼び出す意味はかなり薄くなってしまいます。

なので、GC.Collect() が「なにをどこまでやって」返ってくるのかの説明が必要です。

引用返信 編集キー/
■35268 / inTopicNo.10)  Re[6]: 『「GC.Collect」メソッド』について
□投稿者/ 尾塩 (2回)-(2009/04/23(Thu) 09:30:32)
皆さん、返信ありがとうございます。

今まで、『GC.Collect()』で事足りることが多かったので、
正直『GC.WaitForPendingFinalizers()』の存在は頭になかったです。
教えていただいたサイトを見直すと、スレッドって、説明ありますね・・・(汗)

今、『GC.Collect() 』を明示的に呼び出しているのは、メモリをタイトに使うアプリを開発していて、
システム任せにしておくと、必要なときにメモリが足りなくなることが考えられるからです。
明示的にGCを実行して利用可能なメモリを確保できている処理にして、安心したいわけです。
その手順をはっきりさせたいということです。

今回、GCの動きを探るにあたって、大量なメモリを使用するサンプルとして以下のようなプログラムを作成し検証を行いました。

public static bool InvokeManaged()
{
try
{
for (int i = 0; i < 1000; ++i)
{
List<object> container = new List<object>();

GC.Collect();
※ System.Threading.Thread.Sleep(1);

for (int j = 0; j < 350; ++j)
{
container.Add(new byte[4000000]);
}
}
return true;
}
catch (Exception e)
{
return false;
}
}

試している環境では、※印の行をコメントアウトすると殆ど失敗します。
しかし、※印の行を有効にすると成功するようになります。この動きが?だったので質問させていただいた次第です。タイミングの問題なのかと思い、Sleepの時間を変えてみたりもしたのですが、Sleepの時間そのものには依存関係はなかったです。(試したのは、1〜1000の間でいくつかに刻んでためしました。)

引用返信 編集キー/
■35276 / inTopicNo.11)  Re[7]: 『「GC.Collect」メソッド』について
□投稿者/ 渋木宏明(ひどり) (1124回)-(2009/04/23(Thu) 10:38:59)
渋木宏明(ひどり) さんの Web サイト
> 試している環境では、※印の行をコメントアウトすると殆ど失敗します。
> しかし、※印の行を有効にすると成功するようになります。この動きが?だったので質問させていただいた次第です。
> タイミングの問題なのかと思い、Sleepの時間を変えてみたりもしたのですが、Sleepの時間そのものには依存関係はなかったです。(試したのは、1〜1000の間でいくつかに刻んでためしました。)

単純に、Sleep() を挟むことで円滑にスレッド切り替えが生じて、メモリ回収がスムースに行われただけとか?(byte 配列ではファイナライザは呼び出しは省略されているはず)

仮にそうであるなら、CPU のコア数が増えると、観測される現象は変化するかもしれません。(成功する場合が増える?)

あと、スルーされてましたが

>エスパー解凍すると、メモリー食ってるオブジェクトのDisposeをきちんと呼んでやるといいんじゃないかな。

という点については大丈夫ですか?

引用返信 編集キー/
■35289 / inTopicNo.12)  Re[8]: 『「GC.Collect」メソッド』について
□投稿者/ 尾塩 (3回)-(2009/04/23(Thu) 17:48:36)

> 単純に、Sleep() を挟むことで円滑にスレッド切り替えが生じて、メモリ回収がスムースに行われただけとか?(byte 配列ではファイナライザは呼び出しは省略されているはず)

言われてみるとそんな気がしていました。

>仮にそうであるなら、CPU のコア数が増えると、観測される現象は変化するかもしれません。(成功する場合が増える?)
GCの動きなのでマシンを変えるとこの結果も変わりそうだな、と直感的には思いましたが、まだこのSampleCodeを別のマシンで試してはいません。

> >エスパー解凍すると、メモリー食ってるオブジェクトのDisposeをきちんと呼んでやるといいんじゃないかな。
>
> という点については大丈夫ですか?
すみません。スルーしたはわけではないのですが、

『エスパー解凍』という文言が何を指しているのか私にはちょっと良く分からなくて・・・。

>メモリー食ってるオブジェクトのDisposeをきちんと呼んでやるといいんじゃないかな。
という点に関しては留意しています。

メモリを食っているObjectのDisposeをきちんとするということは意識しています。一応・・・。

先のサンプルコードのような例題を考えたのは、C#開発が初めての開発者に対してDisposeとGC.Collectの説明をするためでした。
コードレビューをしてDisposeされていない箇所が多々あり、Disposeの必要性と、あとGC.Collectの動きを説明するためのTestCodeを書きました。
1つは、Managed Codeに対する、先の投稿のように少しずつメモリーが増えていくような処理の中で、GC.Collectを明示的に呼んで解放している様子
2つめは、Unmanagedであるが、Disposeを呼んでいない場合
3つめは、UnmnanagedでDisposeを呼んでいる場合
です。

今回この流れなの中で、GCの動きが“おやっ”と思ったところがあったというわけでして・・・。

引用返信 編集キー/
■35293 / inTopicNo.13)  Re[9]: 『「GC.Collect」メソッド』について
□投稿者/ Jitta on the way (310回)-(2009/04/23(Thu) 18:15:37)
No35289 (尾塩 さん) に返信
>

>>>エスパー解凍すると、メモリー食ってるオブジェクトのDisposeをきちんと呼んでやるといいんじゃないかな。
>>
>>という点については大丈夫ですか?
> すみません。スルーしたはわけではないのですが、
>
> 『エスパー解凍』という文言が何を指しているのか私にはちょっと良く分からなくて・・・。
エスパー回答の誤字だと思います。書かれていないことを想像して答えると、ということです。




> メモリを食っているObjectのDisposeをきちんとするということは意識しています。一応・・・。

この辺、私の理解とはちょっと違うみたい。
マネージドなメモリを使う限り、disposeする必要はないし、そもそも実装されていないと思います。



> コードレビューをしてDisposeされていない箇所が多々あり、Disposeの必要性と、あとGC.Collectの動きを説明するためのTestCodeを書きました。

んー?
disposeは、「ファイルを開いたら閉じなきゃダメよ」で、GC.Collectは、「おばちゃん、ごめんやけど、こっち先掃除してくれんけ?」じゃないでしょうか。

引用返信 編集キー/
■35313 / inTopicNo.14)  Re[10]: 『「GC.Collect」メソッド』について
□投稿者/ 倉田 有大 (580回)-(2009/04/24(Fri) 08:23:04)
>>『エスパー解凍』という文言が何を指しているのか私にはちょっと良く分からなくて・・・。
> エスパー回答の誤字だと思います。書かれていないことを想像して答えると、ということです。

ども、補足ありがとうございます。
GC.Collectの動作の話が中心だったのね。
>2つめは、Unmanagedであるが、Disposeを呼んでいない場合
呼ばないと、メモリーの量うんぬんよりも、画面を描画するリソースが足りなくなったり、ファイルの場合ファイルが開けなくなったり。

>今、『GC.Collect() 』を明示的に呼び出しているのは、メモリをタイトに使うアプリを開発していて、
>システム任せにしておくと、必要なときにメモリが足りなくなることが考えられるからです。

byte配列なら、使いまわせないかな
GC.Collectしないとメモリー回収がまにあってくれないというのは、かなりメモリー食いなアプリですね。
メモリーを大きく使うタイミングがわかっているのなら、その後にGC.Collect?
サンプルコードを見ていると、単純にGC.Collectするだけじゃ間に合わないほど、メモリーの取得を繰り返す可能性があるのかな。
引用返信 編集キー/
■35375 / inTopicNo.15)  Re[11]: 『「GC.Collect」メソッド』について
□投稿者/ 尾塩 (4回)-(2009/04/28(Tue) 10:30:20)
色々補足ありがとうございます。ちょっと話が細切れに説明したのでいたらなかったと思います。

.NET開発でのメモリ管理というところで、Managed・Unmanaged両方の説明が開発者に必要だったわけです。
そこで、3のCaseを作りました。

いつも思うのですが、ManagedとUnmanagedの話はきっかり別々に話した方がいいですね。混乱してしまいます。


で、ここからは私の理解なので間違っていたら指摘してください。

Managedのメモリは放っておけば、システムが適時GCを呼び出してメモリが解放される。
でも、メモリの使い方がタイトなアプリの場合、システムが適時GC実行することを待っていたら、メモリが必要なときに足りないということが考えられる。
なので以前似たようなアプリの開発したときは、
>メモリーを大きく使うタイミングがわかっているのなら、その後にGC.Collect?
って感じで『GC.Collect』を明示的に呼んでやってました。


それはそれとして、今回の開発者はGCの動きそのものになじみがないから、サンプルコードを使って動きを説明したわけです。
実際の開発中のアプリに今回のサンプルのような仕組みになっているところは直接的にはありません。
『そもそもGCの動きってどうなんだ?』を端的に説明したかったので、メモリを積み重ねるようなコードにしたわけです。
“システムが判断して適切に解放してくれる”って結構曖昧な動きだと思っています。

一方、開発者は、Unmanageのメモリも扱いがはっきりとしていなくて、Unmanage Resourceを使っていればDispose処理が必要だ、
と私は理解しているので、Dispose呼ばないとどういうことになるか、というのが説明したくてCase2になります。

引用返信 編集キー/
■35381 / inTopicNo.16)  Re[12]: 『「GC.Collect」メソッド』について
□投稿者/ 渋木宏明(ひどり) (1129回)-(2009/04/28(Tue) 11:31:32)
渋木宏明(ひどり) さんの Web サイト
> Managedのメモリは放っておけば、システムが適時GCを呼び出してメモリが解放される。

s/システム/ランタイム/

引用返信 編集キー/
■35414 / inTopicNo.17)  Re[12]: 『「GC.Collect」メソッド』について
□投稿者/ Jitta on the way (314回)-(2009/04/29(Wed) 07:52:48)
No35375 (尾塩 さん) に返信

dispose のほうは、P/Invoke を使います。CreateFile 等で、ハンドルを閉じずにいると、メモリーが沢山残っていても確保できなくなります。
メモリは、リソースの一部ですが、全てではありません。
引用返信 編集キー/
■35513 / inTopicNo.18)  Re[13]: 『「GC.Collect」メソッド』について
□投稿者/ 尾塩 (5回)-(2009/05/07(Thu) 10:54:30)
当初の疑問は解決したので、Closeします。
みなさん、ありがとうございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -