- PR -

継承したコントロールのメモリの解放

投稿者投稿内容
KT工房
常連さん
会議室デビュー日: 2006/06/20
投稿数: 49
投稿日時: 2009-02-17 14:15
たくさんの返答ありがとうございます。

こちらでもいろいろと試してみましたが、ワーカーセットのメモリ増加のほうは
マシンの性能によるみたいです。
何台かのPCで試しましたが、性能の良いPCでは増加が少ない傾向でした。

結果としては 渋木宏明(ひどり)さん の言われている通り、
極端なシナリオの場合には対策(Sleepをいれるなど)の必要があるということになるんでしょうかね。

私的にはそんな条件があるぐらいなら、昔のように自分で解放させてくれたほうがありがたいのですが・・・。
やじゅ
常連さん
会議室デビュー日: 2008/07/15
投稿数: 28
お住まい・勤務地: 静岡市
投稿日時: 2009-02-17 19:31
もし、GC.Collect を呼び出すのであれば下記の3行した方がいいみたいです。
http://msdn.microsoft.com/ja-jp/library/ms998547.aspx

System.GC.Collect();
// ファイナライゼーションが終わるまでスレッド待機
System.GC.WaitForPendingFinalizers();
// ファイナライズされたばかりのオブジェクトに関連するメモリを開放
System.GC.Collect();
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2009-02-18 01:13
こんばんは。

すいません。今頃になって現象の検証をしています。

少し遅いコメントですが、
最初にKT工房さんがおっしゃられていた現象について…

引用:

KT工房さんの書き込み (2009-01-27 13:19) より:
メモリが解放されない要因としては、以下の項目が関係しているようです。

最適化が有効であれば、メモリが解放される
(リリースモードはデフォルトで最適化が有効となっている)

デバッグモードでも最適化を有効にすればメモリが解放された。
逆にリリースモードでも最適化を無効にするとメモリが解放されない。



最適化を有効にする場合と、有効にしない場合の生成アセンブリの違いを見ていますが

どうも最適化を有効にしない場合は、VBコンパイラが
作成したクラスのコンストラクタに以下のようなコードを挿入していますね。

コード:
<DebuggerNonUserCode> _
Public Sub New()
    Dim list As List(Of WeakReference) = CustomDataSet.__ENCList
    SyncLock list
        CustomDataSet.__ENCList.Add(New WeakReference(Me))
    End SyncLock
End Sub



これがKT工房さんのおっしゃる現象と関係しているように思います。

ちなみに、C#コンパイラでは最適化に関係なく
上記のようなコードの挿入は見られません。
メモリが解放されない現象も起きないようです。

Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2009-02-18 03:21
引用:

KT工房さんの書き込み (2009-02-02 13:28) より:
さらに調査報告です。
コンパイルオプションも、継承も関係なしに通常のコントロールでも
生成→解放を繰り返すと Out of Memory Exception で落ちることを確認しました。
2G超える前ぐらい?

実メモリに変動は無いのですが、ワーカーセットのメモリが増加していきます。



残念ながら私のPC(CPU:Intel Core2,メモリ2G)では再現しませんでした。ワーキングセットも22MBくらいで安定しちゃってます。
私のPCの性能が良いのでしょうか?(苦笑)

ところで「サーバーGC」にしてみたら、また違う結果になったりしませんかね!?
GCSettings.IsServerGC プロパティ

私の環境ですと、
ワークステーションGCのときに比べサーバーGCのほうが
フルGCの発生がかなり減りましたけれども。

コード:

Debug.WriteLine("IsServerGC = " + GCSettings.IsServerGC().ToString())

Dim lblTest As Label
For i As Integer = 1 To 10000000
lblTest = New Label()
lblTest.Dispose()
Next

Debug.WriteLine("CollectionCount gen0 " + GC.CollectionCount(0).ToString())
Debug.WriteLine("CollectionCount gen1 " + GC.CollectionCount(1).ToString())
Debug.WriteLine("CollectionCount gen2 " + GC.CollectionCount(2).ToString()) ' フルGC



結果1(ワークステーションGC):
IsServerGC = False
CollectionCount gen0 3738
CollectionCount gen1 1869
CollectionCount gen2 5

結果2(サーバーGC):
IsServerGC = True
CollectionCount gen0 1780
CollectionCount gen1 890
CollectionCount gen2 1

考察:
サーバーGCが「Stop the world」方式??だから、回収率が良いということでしょうか。



[ メッセージ編集済み 編集者: Tdnr_Sym 編集日時 2009-02-18 03:41 ]
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2009-02-18 09:49
引用:

結果としては 渋木宏明(ひどり)さん の言われている通り、
極端なシナリオの場合には対策(Sleepをいれるなど)の必要があるということになるんでしょうかね。



↑のように書かれると僕が「Sleep 使え」と言ってるみたいなので、やめてほしいな

前にも書きましたが、Sleep 呼び出しの効果は差が激しすぎるので、およそ「解決策」とは呼べない処置です。

直接的な対策は GC.Collect() 呼び出しですが、馬鹿正直にこれを実行すると、これはパフォーマンス面で相当のペナルティを払うことになります。(極端なシナリオであるならあるほど不利なはず)

なので、現実的には「構造体配列を自分で管理して上手に使う」など、設計レベルで工夫するのが妥当でしょう。
はにまる
ぬし
会議室デビュー日: 2003/12/19
投稿数: 969
お住まい・勤務地: 誤字脱字の国
投稿日時: 2009-02-18 22:28
ご返答ありがとうございます。
引用:

渋木宏明(ひどり)さんの書き込み (2009-02-16 22:46) より:
他プロセスの実行による CPU 負荷やメモリ使用状況などによっても、適切な値は変わるはずなので、静的に値を求めるのは難しいでしょう。


引用:

実行環境によってはまるで効果がないことも十分に考えられる処置です。


環境のバリエーションを色々考えなければならないソフトでは
有効な手立てとは言えませんね。

ただ私の携わっている業務系システム(Webアプリ以外)では
OSのバージョンやハードスペックなど環境状態を限定する事が多く
更に運用状況の制限も設け易いため状況状態は想定し易い背景があります。

外部環境に関係なくGCの気分によって効果が大きく変わるならば困ったものですが。

引用:

いいえ、あります。
GC.Collect() がそれです。


ありがとうございます。

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