goo blog サービス終了のお知らせ 

へたれエンジニア日記(旧跡地)

こちらへ引っ越しました。http://d.hatena.ne.jp/toritori0318/

DataGrid メモリ量の検証

2006-12-17 23:10:40 | .NET C#
自作ツールを使ってた(+テスト中)時の話ですが、
間違えてものすごく大量のデータ
データグリッドに落としてしまったんです。

「うわー、大丈夫かコレ」


その時タスクマネージャーで確認してみると
メモリ使用量が200Mくらいで止まってました。

「あ、そだ。せっかくだからこのままほっといてメモリ量の検証してみようっと」


と思い立ち、取得したデータを上書きして
しばらくほっといたのですが

「全然減らねぇ」


おいおい、大丈夫かよ(;´Д`)
もしかして取得データがどこかに残っててメモリ回収されてないんじゃないの?



というわけ(?)で
データグリッド(正確にはデータセット?)の取得・クリアを繰り返し、
ガベージコレクトできちんと回収されてるかを検証してみました。


*注
 メモリ量の取得には
 GC.GetTotalMemory(false);
 を使用しておりますが、必ずしも正確な値ではないらしいです





それでは以下を見ていきましょう。
画面の「トータルメモリ(初期値)」は、文字通り初期のメモリ値です。
その下の「トータルメモリ」が、その時点でのメモリを表します。
トータルメモリ」の値に注目して下さい。


①初期画面。





まずは、クリアボタンで以下のコードを実行した後で
ガベージコレクトした場合を見てみましょう。
this.dataGrid1.DataSource = null;







②データ取得直後のメモリ
(ちなみに、検証用データとして
 約10万件のデータをデータベースから取得しています。
 30Mくらい?)




そして、クリアボタンを押してからガベージコレクトした結果が③です。

③「クリア」→「ガベージコレクト」した直後のメモリ




②379MB→③243KB
と、だいぶ減りました。取得データが回収されたようです。





それでは次に、クリアボタンで以下のコードを実行した後で
ガベージコレクトした場合を見てみましょう。
DataSet _ds = (DataSet)this.dataGrid1.DataSource;
_ds.Clear();
 
 
 


④データ取得直後のメモリ




⑤「クリア」→「ガベージコレクト」した直後のメモリ




④360MB→⑤270MB
と、今回はあまり減りませんでした。
_ds.Dispose();
  や
 _ds = null;
 を入れても同じ結果にでした)

これ、どうして減らないのかはよくわかりません。
取得したTableやRowは参照外れてるんじゃないの?
どこかに残ってるの?
とりあえずメモリ上に残したくない時は
DataSourceにnullを設定しろということでしょうか。






じゃあ次に、
大きいデータを取得した後に小さいデータをバインドし直した時は
どうなるのか見てみましょう。

まず、10万件ほどのデータをバインドします。

⑥大きいデータ取得直後のメモリ





そして次に、1件だけバインドさせます。

⑦小さいデータ取得直後のメモリ




「382MB」→「383MB」
と、この時点ではメモリは減りません。
そして、上記からガベージコレクトした結果が⑧です。

⑧「ガベージコレクト」(×2)した直後のメモリ





大きいデータ分のメモリは回収されたようです。
ちなみに、ガベージコレクトは2回行わないと回収されませんでした。
世代管理とやらが関係してるとは思うのですが
GC.Collect();
って、すべての世代に対して回収するんじゃないの?
ようわからんの~




最後に
取得したデータをどんどん上書きしていった時のメモリも検証してみました。

38M(取得1回目)→78M(取得2回目)→110M(取得3回目)→145M(取得4回目)
と増やした後にガベージコレクトした結果、
145M→99M(GC1回目)→36M(GC2回目)…(以降変わらず)
とメモリが減っていきました。

う~ん、やっぱり世代毎に回収されるっぽいな。
まあそれはいっか。




今回の検証の結果、とりあえずデータグリッドをクリアしたい場合は
データソースにNullを入れるのが吉のようです。
というか、上書きでもいずれ消えそうだからそれでもいいんですけどね。
DataSetのClearメソッドでは何故ダメなんだろう…謎だ。




ああそうだ。
結局、自作ツールのメモリが減らないのはどうして?
という問いですが…

.NETとは

そういうもの

らしいですw


減らなくて正解。
ガベージコレクトして減っていれば問題ないということでしょう(多分)

しかしDisposeもそうだけど
メモリ管理本当わからんな~



…こんなんでいいのか?

Disposeについて

2006-09-10 23:41:51 | .NET C#
色々な掲示板で勉強中です。

頭が痛い。

結局わかってなさそうだし。





今回はオブジェクトの解放について。

アプリ作ってるのでその辺もちゃんと勉強しなきゃなー
と思ってるのですが、わけわかめです。

用は、オブジェクトの解放って
いつどのようにするのか?
newする度にする必要があるのか?


という疑問から

いろいろと調査したのです。





基本的な考え方として

アンマネージソースのオブジェクトはDisposeを行う

という記述が多々見られたのですが…


アンマネージソースって何(・ω・)?






しかも!どこにもないんですよねー、説明が。
教えてくれるような人もいないし…




ネット上をかなり探し回って見つけたのが

「CLRが管理できないメモリ」のこと

意味わかんねー!




具体的な説明が欲しい…


でさらに探し回った結果

「ファイルアクセス・DBアクセス・ウィンドウ・フォント・画像」など、メモリ以外の資源

という記述を発見しました。


なんとなーくわかった気がします。
ということで勉強再開。




まず、確実にしないといけない場合。

■ファイルアクセス
■(ダイアログ)フォーム
■DBアクセス

それ以外では
Font、Graphicsをnewした後、
Imageを取り込んだ後などが該当するようです。
こんなときは、明示的にDisposeしてあげましょう。




あと、このような疑問もありました。

①null代入とDisposeの違いは?
②親コントロールがDisposeされた時、子コントロールもDisposeされるか?
③ローカルメソッドなどでnewされたローカルオブジェクトはDisposeされるか?
④コントロールにAddしたオブジェクトはDisposeされるか?



null代入 → 参照の破棄
Dispose → リソース(資源)の破棄
ということらしいです。
結果的にどう異なるのかはわかりません(泣)



これは「○」。
Formに乗っかってるTextBoxなどのオブジェクトも解放します(そりゃそうだよな)



これも「○」。
private void testMethod ()
{
DataSet ds = new DataSet();
}
このようなメソッドを呼んだとしても、抜けた時点で解放します(そりゃそうだよな)


これは「?」。
例えば、実例を出しますと
まず、UserControlA を乗せている TabPageB があります。
そして、TabControlCにTabPageBをAddします。
その後、TabControlCからTabPageBをRemoveします。

すると… UserControlA のDisposeがスルーしてしまいました。

ということは、解放されないってことだよなぁ…
でも、本当はするんじゃないかなぁと思ったり。


この辺は次回実験してみます。

.NET スプリッタコントロールの使い方

2006-08-27 14:01:33 | .NET C#
待望(?)の.NETTips!
今回はスプリッタコントロールの使用方法。
あんまり無いような気がするので。

スプリッタというのは、
複数のウィンドウの境目をグイーンとドラッグして
大きさを変えたりするようなオブジェクトですね。
あると便利。

こんなの



スプリッタコントロールを使用する場合、基本的には

 ①親パネル
 ②左(上)に配置するオブジェクト
 ③スプリッタコントロール
 ④右(下)に配置するコントロール

の4つを1くくりとします。
①の上に②③④を乗っけるイメージですね。


分割した中で、さらに分割したウィンドウを作りたい場合は
上記の②か④を親パネルとして、その親パネルの中にコントロールを配置していきます。


それでは、実際に上記で紹介したアプリケーションを作ってみましょう。
手順は以下の通りになります。





はい。まっさらなフォーム。




メインとなるパネルをぽちっと貼り付けます。
わかりやすく枠を設定します。




メインパネルの上に

・左側に配置するパネル(A)
・左右移動させるためのスプリッタ(B)
・右側に配置するパネル(C)

を配置します。
わかりやすくするために
色分けしてみました。




まず、左に配置するパネル(A)を選択し、「最前面へ移動」を選択します。
*この「最前面へ移動」の順番が重要!



プロパティの「Dock」を「Left」にします。

次に、スプリッタ(B)も④→⑤の流れで設定していきます。
更に、右側に配置するパネル(C)も④→⑤の流れで設定しますが、
この最後のオブジェクトだけは「Dock」を「Fill」に設定しましょう





上記まで終了すると、図のようになるはずです




次に、縦のスプリットを設定します。
②で設定した時同様、
右側に配置したパネル(C)の上に、
・上側に配置するパネル(A')
・上下移動させるためのスプリッタ(B')
・下側に配置するパネル(C')
 を配置します。




④~⑤と同じ手順で、順番通りにA'、B'、C'と設定していきます。
(縦スプリットなので、「Dock」は「Top」にしましょう)




これでアプリケーションが完成しました!
 ウィンドウをごりごり動かしてみてください!