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

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

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

メモリリークに関して

[トピック内 42 記事 (1 - 20 表示)]  << 0 | 1 | 2 >>

■91509 / inTopicNo.1)  メモリリークに関して
  
□投稿者/ kiku (98回)-(2019/07/02(Tue) 17:06:54)

分類:[C#] 

下記環境にて下記ソース(全体の抜粋)で、フォームのnewとdisposeを繰り返すと、
メモリ使用量がどんどん増加し、OS全体が不安定になる現象が発生しています。
下記ソースの★の部分を追加すると、メモリ使用量の増加が抑えられ、
OSが不安定になることはなくなりました。
※通常はGCで回収されると思っているのですが、
 その発生を確認することができなかった。
※通常はControls.Addしているのでフォーム解放時にdisposeされる認識

そこで質問です。

質問1
 本当にフォントやラベルが解放されないことを確認する方法はないでしょうか?

質問2
 解放されないことが確認できたとして、
 ★の部分の対処は適切でしょうか? 

●環境
 .NETCompactFramework3.5
 WindowsCE6.0
 C#
 VS2008

●ソース
    partial class Test
    {
        /// <summary>
        /// 必要なデザイナ変数です。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
                this.lbl_serialnumber.Font.Dispose();//★対策追加
                this.lbl_serialnumber.Dispose();//★対策追加
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナで生成されたコード

        /// <summary>
        /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディタで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();//★対策追加
            this.lbl_serialnumber = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // lbl_serialnumber
            // 
            this.lbl_serialnumber.BackColor = System.Drawing.Color.White;
            this.lbl_serialnumber.Font = new System.Drawing.Font("MS ゴシック", 16F, System.Drawing.FontStyle.Bold);
            this.lbl_serialnumber.Location = new System.Drawing.Point(5, 115);
            this.lbl_serialnumber.Name = "lbl_serialnumber";
            this.lbl_serialnumber.Size = new System.Drawing.Size(310, 20);
            this.lbl_serialnumber.Text = "lbl_serialnumber";
            // 
            // Test
            // 
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
            this.ClientSize = new System.Drawing.Size(640, 480);
            this.ControlBox = false;
            this.Controls.Add(this.lbl_serialnumber);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "Test";
            this.Text = "Test";
            this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
            this.ResumeLayout(false);
        }

        #endregion

        private System.Windows.Forms.Label lbl_serialnumber;
    }

引用返信 編集キー/
■91511 / inTopicNo.2)  Re[1]: メモリリークに関して
□投稿者/ PANG2 (308回)-(2019/07/02(Tue) 17:53:27)
No91509 (kiku さん) に返信
> 質問1
>  本当にフォントやラベルが解放されないことを確認する方法はないでしょうか?

WeakReferenceを List<WeakReference> に貯蔵して終了時に確認する。

http://pro.art55.jp/?eid=1109086
引用返信 編集キー/
■91513 / inTopicNo.3)  Re[1]: メモリリークに関して
□投稿者/ WebSurfer (1852回)-(2019/07/02(Tue) 18:32:00)
No91509 (kiku さん) に返信

質問に対する直接の回答にはなってないですが・・・

> 質問1
>  本当にフォントやラベルが解放されないことを確認する方法はないでしょうか?

System.Drawing.Font クラスなどアンマネージドリソースを保持しているクラスは、IDisposable
インターフェイスを継承して Dispose パターンを使った実装がされていて、Dispose メソッドで
アンマネージドリソースを開放できるようになっているはずです。

アンマネージドリソースは GC では解放されないので、確認するまでもなく Dispose しなければ
解放されてないはずです。

なので、IDisposable インターフェイス を継承して Dispose メソッドを実装しているクラスは全
て、そのオブジェクトが使用されなくなった時点で Dispose メソッドを呼び出すべきということ
のようです。

詳しくは、自分のブログで恐縮ですか、以下の記事を見てください。

Dispose パターン
http://surferonwww.info/BlogEngine/post/2019/05/31/dispose-pattern.aspx


> 質問2
>  解放されないことが確認できたとして、
>  ★の部分の対処は適切でしょうか? 

上で紹介した記事に書いてある通り「Dispose パターン」というものがあります。Visual Studio
で雛形を自動生成してくれますので、それを使うのがよさそうです。

自分で作るカスタムクラスの場合、マネージドリソースしか保持しない場合は Dispose パターン
の実装は不要です。必要なのはアンマネージドリソースを保持する場合のみですが、それには以下
のケースがあると思います。

(1) Dispose パターンを実装した .NET のクラスのインスタンスを保持している。

(2) クラス内でアンマネージドリソースを取得し、それを保持している。

プログラマが Dispose するのを忘れた場合でも GC は働きます。GC ではアンマネージドリソース
は開放できませんが、その際ファイナライザが呼び出されます。なので、Dispose パターンによっ
て、最悪でもファイナライザでアンマネージドリソースを開放できるようにします。

引用返信 編集キー/
■91515 / inTopicNo.4)  Re[1]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 (2213回)-(2019/07/02(Tue) 20:22:22)
No91509 (kiku さん) に返信
> ※通常はControls.Addしているのでフォーム解放時にdisposeされる認識

.NET Framework の場合、Control.Dispose(bool) メソッド内で

 For i = 0 To controlCollection.Count - 1
  Dim control As Control = controlCollection(i)
  control.Parent = Nothing
  control.Dispose()
 Next

に相当する処理が行われていますね。
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,6040


ところが .NET Compact Framework の実装については、上記に相当する処理を見つけることができませんでした。
(一応 Dispose 時には、自身のハンドルを AGL.DLL の 序数2 (Destroy API) に渡しているようですが…)
引用返信 編集キー/
■91516 / inTopicNo.5)  Re[2]: メモリリークに関して
□投稿者/ kiku (99回)-(2019/07/03(Wed) 09:06:41)
No91511 (PANG2 さん) に返信
> ■No91509 (kiku さん) に返信
>>質問1
>> 本当にフォントやラベルが解放されないことを確認する方法はないでしょうか?
>
> WeakReferenceを List<WeakReference> に貯蔵して終了時に確認する。
>
> http://pro.art55.jp/?eid=1109086

弱い参照を保持することが出来るんですね。
ご案内頂きありがとうございます。
試してみます。
引用返信 編集キー/
■91517 / inTopicNo.6)  Re[2]: メモリリークに関して
□投稿者/ kiku (100回)-(2019/07/03(Wed) 09:10:40)
No91513 (WebSurfer さん) に返信
> ■No91509 (kiku さん) に返信
>
> 質問に対する直接の回答にはなってないですが・・・

下記1と2を混同していたため、混乱させてしまいました。
通常下記2でVusualStudiooの自動生成されたソース内の
Control.Addされるため、Control.Dispose内で自動的に
マネージドリソースもdisposeされる認識でした。
下記1は関係ないですね。

1.
>※通常はGCで回収されると思っているのですが、
> その発生を確認することができなかった。

2.
>※通常はControls.Addしているのでフォーム解放時にdisposeされる認識

引用返信 編集キー/
■91518 / inTopicNo.7)  Re[2]: メモリリークに関して
□投稿者/ kiku (101回)-(2019/07/03(Wed) 09:14:41)
No91515 (魔界の仮面弁士 さん) に返信
> ■No91509 (kiku さん) に返信
>>※通常はControls.Addしているのでフォーム解放時にdisposeされる認識
>
> .NET Framework の場合、Control.Dispose(bool) メソッド内で
>
>  For i = 0 To controlCollection.Count - 1
>   Dim control As Control = controlCollection(i)
>   control.Parent = Nothing
>   control.Dispose()
>  Next
>
> に相当する処理が行われていますね。
> https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,6040
>
>
> ところが .NET Compact Framework の実装については、上記に相当する処理を見つけることができませんでした。
> (一応 Dispose 時には、自身のハンドルを AGL.DLL の 序数2 (Destroy API) に渡しているようですが…)

回答ありがとうございます。
なんと、これが直接的な原因みたいですね。
.NET Compact FrameworkのControl.Disposeのソースは、
.NET Frameworkのように閲覧可能なのでしょうか?
もし可能ならURL教えて頂けませんか?
自分の目でも確認したいです。

引用返信 編集キー/
■91519 / inTopicNo.8)  Re[3]: メモリリークに関して
□投稿者/ WebSurfer (1853回)-(2019/07/03(Wed) 11:43:19)
No91517 (kiku さん) に返信

自信度は高くなく間違っているかもしれませんので、以下のレスはそのつもりで見てください。

> 通常下記2でVusualStudiooの自動生成されたソース内の
> Control.Addされるため、Control.Dispose内で自動的に
> マネージドリソースもdisposeされる認識でした。

Form コントロールの場合、Dispose(), Dispose(bool) の実装は Control toha違うようです。
下のリンクをクリックして画像を見てください。

http://surferonwww.info/BlogEngine/image.axd?picture=2019%2f7%2fFormDispose0.jpg

VS2008 のヘルプの画像ですが、このあたりはたぶん最新版でも同じはずです。Form に実装され
ている Dispose() と Dispose(boolean) は上の画像の一番上と一番下のものです。

一番下の Form.Dispose (Boolean) メソッドには CE がサポートしているというアイコンがない
のに注目してください。

CE はどうしているかは不明ですが、魔界の仮面弁士さんのレスにある Control.Dispose(bool)
メソッド(画像の上から 3 番目のもの)を使っているのではなかろうかと思われます。

そうであれば、Dispose パターンが実装されていれば、Dispose メソッドを呼び出せば上の画像の
ヘルプの Control.Dispose(Boolean) メソッドの解説に書いてあったように、

"Control とその子コントロールが使用しているアンマネージ リソースを解放します。オプションで、
マネージ リソースも解放します。"

・・・となると思われます。


CE は関係なさそうですが、ご参考までに上の画像のヘルプの Form.Dispose メソッド (Boolean)
の解説の抜粋も書いておきますね。

*** Quote ***
Form で使用されていたリソース (メモリを除く) を解放します。

このメソッドは、パブリック メソッド Dispose と Finalize メソッドによって呼び出されます。
Dispose は、disposing パラメータに true を設定して、プロテクト メソッド Dispose(Boolean)
を呼び出します。Finalize は、disposing に false を設定して、Dispose を呼び出します。

disposing パラメータが true の場合、このメソッドは、この Form から参照されるすべての
マネージ オブジェクトが保持しているリソースをすべて解放します。このメソッドは、参照
される各オブジェクトの Dispose メソッドを呼び出します。

Dispose は、フォームが Show メソッドを使用して表示した場合に自動的に呼び出されます。
ShowDialog などの別のメソッドを使用する場合、またはフォームがまったく表示されない場合は、
アプリケーション内で Dispose を明示的に呼び出す必要があります。
*** Unqoute ***

ちなみに、CE 版でない通常の Windows Forms アプリが Form.Dispose (Boolean) メソッドを
使っていることは間違いなさそうです。

http://surferonwww.info/BlogEngine/image.axd?picture=2019%2f7%2fFormDispose1.jpg

質問者さんの CE 版の場合はどうなってるでしょう?
引用返信 編集キー/
■91520 / inTopicNo.9)  Re[3]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 (2214回)-(2019/07/03(Wed) 13:17:52)
2019/07/03(Wed) 13:23:45 編集(投稿者)

No91509 (kiku さん) に返信
> ※通常はGCで回収されると思っているのですが、

.NET Compact Framework の GC は、コンパクションありのマーク・アンド・スイープ GC ですが、
.NET Framework の GC は 世代別 GC 実装であり、そもそも管理方法からして別物だそうな。

https://blogs.msdn.microsoft.com/stevenpr/2004/07/26/an-overview-of-the-net-compact-framework-garbage-collector/
https://blogs.msdn.microsoft.com/stevenpr/2005/12/14/the-design-of-the-net-compact-framework-clr-part-iii-gc-heap-management/

https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals

https://ja.wikipedia.org/wiki/%E3%82%AC%E3%83%99%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3
https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%BB%E3%82%A2%E3%83%B3%E3%83%89%E3%83%BB%E3%82%B9%E3%82%A4%E3%83%BC%E3%83%97
https://ja.wikipedia.org/wiki/%E4%B8%96%E4%BB%A3%E5%88%A5%E3%82%AC%E3%83%99%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3


追記でこちらも。
なお、下記の「コンカレント GC」は、docs.microsoft.com では
「同時実行ガベージ コレクション」と訳されているようです。
http://www.stprec.co.jp/ceblog/2012/05/14/




No91518 (kiku さん) に返信
> .NET Compact FrameworkのControl.Disposeのソースは、
> .NET Frameworkのように閲覧可能なのでしょうか?

いいえ。
引用返信 編集キー/
■91521 / inTopicNo.10)  Re[4]: メモリリークに関して
□投稿者/ kiku (102回)-(2019/07/03(Wed) 13:26:36)
No91519 (WebSurfer さん) に返信
> 質問者さんの CE 版の場合はどうなってるでしょう?

回答になっているかわかりませんが、
Formは下記のようにusingにて囲っているため、
必ずフォームのDipposeが呼ばれる認識でいます。

            using (var form = new Test())
            {
                form.ShowDialog();
            }

引用返信 編集キー/
■91522 / inTopicNo.11)  Re[5]: メモリリークに関して
□投稿者/ WebSurfer (1854回)-(2019/07/03(Wed) 14:40:36)
No91521 (kiku さん) に返信

>>質問者さんの CE 版の場合はどうなってるでしょう?

その意味は、上の No91519 に張ったリンク先の画像(下に再掲)で base.Dispose(disposing; にカーソ
ルを当てて見ると、自分の環境では Form.Disposing(bool) となっていますが、質問者さんの CE 環境で
はどうなってますかという意味です。

http://surferonwww.info/BlogEngine/image.axd?picture=2019%2f7%2fFormDispose1.jpg

CE では Form.Disposing(bool) はサポートされてないそうなので。


> Formは下記のようにusingにて囲っているため、
> 必ずフォームのDipposeが呼ばれる認識でいます。
> 
>             using (var form = new Test())
>             {
>                 form.ShowDialog();
>             }
> 

Dispose は using を抜けるときに呼ばれると自分も思います。でも、No91519 で書きました、

> Dispose は、フォームが Show メソッドを使用して表示した場合に自動的に呼び出されます。
> ShowDialog などの別のメソッドを使用する場合、またはフォームがまったく表示されない場合は、
> アプリケーション内で Dispose を明示的に呼び出す必要があります。

の「ShowDialog などの別のメソッドを使用する場合」が気になります。気にしすぎだろうとは思
いますが。


引用返信 編集キー/
■91523 / inTopicNo.12)  Re[4]: メモリリークに関して
□投稿者/ kiku (103回)-(2019/07/03(Wed) 16:50:44)
No91520 (魔界の仮面弁士 さん) に返信
> 2019/07/03(Wed) 13:23:45 編集(投稿者)
>
> ■No91509 (kiku さん) に返信
>>※通常はGCで回収されると思っているのですが、
>
> .NET Compact Framework の GC は、コンパクションありのマーク・アンド・スイープ GC ですが、
> .NET Framework の GC は 世代別 GC 実装であり、そもそも管理方法からして別物だそうな。
> 追記でこちらも。
> なお、下記の「コンカレント GC」は、docs.microsoft.com では
> 「同時実行ガベージ コレクション」と訳されているようです。
> http://www.stprec.co.jp/ceblog/2012/05/14/

ありがとうございます。
助かります。

> ■No91518 (kiku さん) に返信
>>.NET Compact FrameworkのControl.Disposeのソースは、
>>.NET Frameworkのように閲覧可能なのでしょうか?
>
> いいえ。

残念。
実機で確認するしかなさそうです。



Labelであるlbl_serialnumberのDisposeを実行した場合、
lbl_serialnumber.FontのDisposeが実行されるのかどうかの
確認ができていない状況です。
PANG2さんからご紹介頂いたWeakReferenceを使ってみたのですが、
うまく判断できないように見えました。
Fontを継承したクラスを作ってDisposeが呼ばれることを
検証しようとしたのですが、Fontは継承禁止のクラスのようで
この方法での確認ができませんでした。

困りました。
引用返信 編集キー/
■91524 / inTopicNo.13)  Re[5]: メモリリークに関して
□投稿者/ Hongliang (835回)-(2019/07/03(Wed) 17:04:10)
2019/07/03(Wed) 17:04:43 編集(投稿者)

> Labelであるlbl_serialnumberのDisposeを実行した場合、
> lbl_serialnumber.FontのDisposeが実行されるのかどうかの
> 確認ができていない状況です。

それはされないと断言していいでしょう。
特にControlのFontなど、共有することが多いので、あるコントロールが不要になったからと言って他のコントロールにとって不要かどうかは、そのコントロール自身は知る術がありません。
そのため勝手にDisposeはしない(できない)、と考えられます。
引用返信 編集キー/
■91525 / inTopicNo.14)  Re[4]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 (2215回)-(2019/07/03(Wed) 17:07:14)
No91519 (WebSurfer さん) に返信
> Form コントロールの場合、Dispose(), Dispose(bool) の実装は Control toha違うようです。
> 下のリンクをクリックして画像を見てください。
> http://surferonwww.info/BlogEngine/image.axd?picture=2019%2f7%2fFormDispose0.jpg

Dispose の実装は、Label と Form で差異が無さそうに見えます。
少なくとも .NET Compact Framework においては。


手元の VS2008 環境で、Form1 の継承関係をたどってみました。
CE 機が無いので、エミュレーターでの動作ですけれども。


 × は Dispose(boolean) が実装されていないクラス
 ※ は Dispose(boolean) が派生元にあるが、実装しなおしてないクラス
 ☆ は Sub Overrides Dispose(boolean) として実装しなおされているクラス
 ★ は Sub Overridable Dispose(boolean) として最初に実装されているクラス


.NET Compact Framework の場合

× Object
└× MarshalByRefObject
 └★ Component
  └☆ Control
   └※ ScrollableControl
    └※ ContainerControl
     └※ Form
      └☆ Form1


.NET Framework の場合

× Object
└× MarshalByRefObject
 └★ Component
  └☆ Control
   └☆ ScrollableControl
    └☆ ContainerControl
     └☆ Form
      └☆ Form1



Imports System.Reflection

Public Class Form1
  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    ListBox1.DataSource = New Type(-1) {}
    ListBox1.DisplayMember = "Name"
    ListBox2.DataSource = New Type(-1) {}
    ListBox2.DisplayMember = "Name"
    ComboBox1.DataSource = New Type() { _
      GetType(Form1), _
      GetType(Form), _
      GetType(Label), _
      GetType(Panel), _
      GetType(Control) _
    }
    ComboBox1.DisplayMember = "FullName"
    TextBox1.Text = "Dispose"
    CheckBox1.Checked = True
  End Sub

  Private Sub Analyze(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged, CheckBox1.CheckStateChanged, TextBox1.TextChanged
    Analyze()
  End Sub

  Private Sub Analyze()
    Dim selType As Type = DirectCast(ComboBox1.SelectedItem, Type)
    Dim methodName As String = TextBox1.Text
    Dim declaredOnly As Boolean = CheckBox1.Checked
    CheckBox1.Enabled = Not String.IsNullOrEmpty(methodName)

    'Sub Dispose() の実装
    ListBox1.DataSource = EnumrateMethodImpl(selType, declaredOnly, methodName)

    'Sub Dispose(Boolean) の実装
    ListBox2.DataSource = EnumrateMethodImpl(selType, declaredOnly, methodName, GetType(Boolean))
  End Sub

  Public Shared Function EnumrateMethodImpl(ByVal t As Type, ByVal declaredOnly As Boolean, ByVal methodName As String, ByVal ParamArray params As Type()) As Type()
    Dim bf As BindingFlags = BindingFlags.NonPublic Or BindingFlags.Public Or BindingFlags.Instance
    If declaredOnly Then
      bf = bf Or BindingFlags.DeclaredOnly
    End If
    Dim targetType As Type = t
    Dim types As New List(Of Type)()
    Dim bt As Type = targetType
    Do
      If String.IsNullOrEmpty(methodName) Then
        types.Add(bt)
      ElseIf bt.GetMethod(methodName, bf, Type.DefaultBinder, params, Nothing) IsNot Nothing Then
        types.Add(bt)
      End If
      bt = bt.BaseType
    Loop Until bt Is Nothing
    Dim result As Type() = types.ToArray()
    Array.Reverse(result)
    Return result
  End Function
End Class
引用返信 編集キー/
■91526 / inTopicNo.15)  Re[6]: メモリリークに関して
□投稿者/ kiku (104回)-(2019/07/03(Wed) 17:09:11)
No91522 (WebSurfer さん) に返信
> その意味は、上の No91519 に張ったリンク先の画像(下に再掲)で base.Dispose(disposing; にカーソ
> ルを当てて見ると、自分の環境では Form.Disposing(bool) となっていますが、質問者さんの CE 環境で
> はどうなってますかという意味です。
> http://surferonwww.info/BlogEngine/image.axd?picture=2019%2f7%2fFormDispose1.jpg
> CE では Form.Disposing(bool) はサポートされてないそうなので。

画像が貼れなく手打ちになってしまったため、スペルミスがあるかもしれませんが、
下記の説明が出ていました。
Releases the unmanaged resources used by the System.Windows.Forms.Control and its child controls and optionally releases the managed resources.


> の「ShowDialog などの別のメソッドを使用する場合」が気になります。気にしすぎだろうとは思
> いますが。

なるほど、Formのdisposeが呼ばれていない可能性があるということですね。
考えに入れることにします。
disposeが呼ばれたがどうかの確認がやっぱり必要になってきそうです。
これが今うまくできずに困っています。

引用返信 編集キー/
■91527 / inTopicNo.16)  Re[6]: メモリリークに関して
□投稿者/ kiku (105回)-(2019/07/03(Wed) 17:20:05)
No91524 (Hongliang さん) に返信
> 2019/07/03(Wed) 17:04:43 編集(投稿者)
>
>>Labelであるlbl_serialnumberのDisposeを実行した場合、
>>lbl_serialnumber.FontのDisposeが実行されるのかどうかの
>>確認ができていない状況です。
>
> それはされないと断言していいでしょう。
> 特にControlのFontなど、共有することが多いので、あるコントロールが不要になったからと言って他のコントロールにとって不要かどうかは、そのコントロール自身は知る術がありません。
> そのため勝手にDisposeはしない(できない)、と考えられます。

なるほど、確かにそうですね。
納得しました。
引用返信 編集キー/
■91528 / inTopicNo.17)  Re[5]: メモリリークに関して
□投稿者/ kiku (106回)-(2019/07/03(Wed) 17:43:52)
No91525 (魔界の仮面弁士 さん) に返信
> ■No91519 (WebSurfer さん) に返信
>>Form コントロールの場合、Dispose(), Dispose(bool) の実装は Control toha違うようです。
>>下のリンクをクリックして画像を見てください。
>>http://surferonwww.info/BlogEngine/image.axd?picture=2019%2f7%2fFormDispose0.jpg
> Dispose の実装は、Label と Form で差異が無さそうに見えます。
> 少なくとも .NET Compact Framework においては。

わざわざ検証いただきありがとうございます。

> 手元の VS2008 環境で、Form1 の継承関係をたどってみました。
> CE 機が無いので、エミュレーターでの動作ですけれども。

貼って頂いたソースを実機で動かすことはできましたが
その結果をどのように掲示板に報告するのが良いのか
ソースの内容を理解できていないため、
わかりませんでした。
継承関係を同じように報告すべきだとは思うのですが、
ごめんなさい。

引用返信 編集キー/
■91529 / inTopicNo.18)  Re[3]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 (2216回)-(2019/07/03(Wed) 19:14:23)
No91518 (kiku さん) に返信
>> For i = 0 To controlCollection.Count - 1
>>  Dim control As Control = controlCollection(i)
>>  control.Parent = Nothing
>>  control.Dispose()
>> Next
>> .NET Compact Framework の実装については、上記に相当する処理を見つけることができませんでした。
> なんと、これが直接的な原因みたいですね。

Control.Dispose が上記を実装していれば、
Form1 が Dispose される時に、Controls が再帰的に列挙され、
Panel や Label といった子(あるいは子孫)コントロールも
連動して解放されるはずなのですけれどね。


とはいえ、GC 管理の仕組みがそもそも異なるので、
.NET Framework 版との単純な比較はできません。
(ソースコードは非公開ですし、逆アセンブルはライセンス上禁じられている…)

実際 CE においても、親 Form が Dispose されれば、その配下のコントロール群も
Dispose されることを確認できました。
とはいえ、子コントロールの Dispose が確実に呼ばれているのか、
たまたま GC されただけなのかまでは分かりません。

一応、Dispose(False)時は、ApplicationThreadContext のファイナライズキューへの
登録っぽい処理が行われると聞いたことがありますが、詳しいことは分からないです。


CE 環境は Application.OpenForms でキャッシュされることが無いとは言え、
 (1) VB 特有の「My.Forms.フォーム名」プロパティでキャッシュされることはある
 (2) Form 右上の×を押しても Minimize されるだけ(Close/Dispose されるとは限らない)
ということで、Dispose が呼ばれにくい状況に陥りやすそうではありますね。



あと、経験則的なところで言えば、.NET Compact Framework 環境の場合、
オブジェクトの使用後は、参照変数に Nothing 代入を積極的に行った方が良いみたいです。


[宇宙仮面の研究室] - [Compact Framework のメモリ管理]
https://uchukamen.wordpress.com/2007/01/23/compact-framework-%e3%81%ae%e3%83%a1%e3%83%a2%e3%83%aa%e7%ae%a1%e7%90%86/

[SWK623] - [[.NET] WindowsMobile上のC#アプリのメモリリークを解決せよ!]
https://swk623.com/2009/03/16/net-windowsmobile%E4%B8%8A%E3%81%AEc%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%AF%E3%82%92%E8%A7%A3%E6%B1%BA%E3%81%9B%E3%82%88%EF%BC%81/

[スラド] - [#1411305]
https://srad.jp/comment/1411305


基本的には、変数のスコープを抜ければ GC 対象にはなりそうですが、
Module / Shared 等で保持されたインスタンスや、
メインフォームのフィールド変数がもつ参照の場合は、
変数の寿命が長くなるので、自分で管理するしか無さそう。
引用返信 編集キー/
■91530 / inTopicNo.19)  Re[6]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 (2217回)-(2019/07/03(Wed) 19:37:39)
No91528 (kiku さん) に返信
> その結果をどのように掲示板に報告するのが良いのか
> ソースの内容を理解できていないため、
> わかりませんでした。

とりあえず、Compact か否かで結果が異なるという程度の理解でも可ということで。
Fx と CFx で結果が異なることを確認しておきたかっただけです。
(これも逆アセンブル扱いになるのだろうか…?)


.NET Framework の場合、Reference Source を見れば、
Form.Dispose(bool) が実装されていて、
その中で、ContainerControl.Dispose(bool) が呼ばれていることがわかります。
そしてそれが、Control.Dispose(bool) を呼び出していることも。
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Form.cs,3793


一方 .NET Comapct Framwork の場合、ソースコードは提供されていませんが、
No91525 の実験結果から、Form.Dispose(bool) は特に存在しておらず、
直接 Control.Dispose(bool) がそのまま呼ばれていることが確認できました。



No91529 (魔界の仮面弁士) に追記
> あと、経験則的なところで言えば、.NET Compact Framework 環境の場合、
> オブジェクトの使用後は、参照変数に Nothing 代入を積極的に行った方が良いみたいです。

Nothing の代入有無で、本当に解放状況が変化するかどうかを確認してみました。


端末を再起動して、空きメモリが十分に確保されている状態において、
Button1 でモードレス フォームを表示させた後、
Button2 でそれを Close させています。

すると、Close 直後の ★の行の Nothing 代入がコメントアウトされていた場合、
Form2 および LabelEx が直ちに Dispose されないケースが確認できました。

しかし Nothing 代入するようにした場合は、Button2 の時点ですぐに Dispose されるようになりました。


Partial Public Class Form1
  Inherits Form

  Private f As Form2 = Nothing

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    f = New Form2()
    f.Show()
  End Sub

  Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    If f IsNot Nothing AndAlso Not f.IsDisposed Then
      f.Close()
      'f.Dispose()

      'f = Nothing  '★
    End If
  End Sub
End Class

Public NotInheritable Class LabelEx
  Inherits Label
  Public ReadOnly Id As Long
  Public Sub New()
    Id = Now.Ticks
    Trace.WriteLine(String.Format("==> {0} 生成 {1}", MyClass.GetType().Name, Id))
  End Sub
  Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    MyBase.Dispose(disposing)
    Trace.WriteLine(String.Format("<== {0} 破棄 {1} ({2})", MyClass.GetType().Name, Id, disposing))
  End Sub
End Class

Partial Public Class Form2
  Inherits Form
  Public ReadOnly Id As Long
  Public Sub New()
    Id = Now.Ticks
    InitializeComponent()
    Trace.WriteLine(String.Format("==> {0} 生成 {1}", MyClass.GetType().Name, Id))
  End Sub
  Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
    MyBase.OnLoad(e)
    Controls.Add(New LabelEx() With {.Text = .Id.ToString()})
    SendToBack()
  End Sub
  Private Sub Form2_Disposed(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Disposed
    Trace.WriteLine(String.Format("<== {0} 破棄 {1}", MyClass.GetType().Name, Id))
  End Sub
  Protected Overrides Sub OnDoubleClick(ByVal e As System.EventArgs)
    MyBase.OnDoubleClick(e)
    Close()
  End Sub
End Class



ただ、何度も実験を繰り返すと空きメモリが減ってくるようで、
その場合はどうやら GC が発動するらしく、Nothing 代入せずとも
Dispose される結果になるようです。
引用返信 編集キー/
■91531 / inTopicNo.20)  Re[5]: メモリリークに関して
 
□投稿者/ PANG2 (309回)-(2019/07/03(Wed) 23:35:41)
No91523 (kiku さん) に返信
> PANG2さんからご紹介頂いたWeakReferenceを使ってみたのですが、
> うまく判断できないように見えました。

WindowsとCEで差異はありますか?

public static class WeakReferenceList
{
private class pair
{
public string name;
public WeakReference wRef;
}
private static readonly List<pair> list = new List<pair>();

public static void Add(Control c)
{
list.Add(new pair() {name = c.Name, wRef = new WeakReference(c) });
}

public static string dump()
{
var sw = new System.IO.StringWriter();
foreach (pair c in list) {
sw.Write(c.name);
if (c.wRef.IsAlive) {
sw.Write("(a)");
if (((Control)c.wRef.Target).IsDisposed)
sw.Write("(d)");
}
sw.WriteLine("");
}
return sw.ToString();
}
}

private void Form1_Load(object sender, EventArgs e)
{
WeakReferenceList.Add(this);
WeakReferenceList.Add(label1);
WeakReferenceList.Add(textBox1);
}

static void Main()
{
Application.Run(new Form1());

MessageBox.Show(WeakReferenceList.dump());
GC.Collect();
MessageBox.Show(WeakReferenceList.dump());
}

引用返信 編集キー/

このトピックをツリーで一括表示

次の20件>
トピック内ページ移動 / << 0 | 1 | 2 >>

管理者用

- Child Tree -