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

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

ログ内検索
  • キーワードを複数指定する場合は 半角スペース で区切ってください。
  • 検索条件は、(AND)=[A かつ B] (OR)=[A または B] となっています。
  • [返信]をクリックすると返信ページへ移動します。
キーワード/ 検索条件 /
検索範囲/ 強調表示/ ON (自動リンクOFF)
結果表示件数/ 記事No検索/ ON
大文字と小文字を区別する

No.91509 の関連記事表示

<< 0 | 1 | 2 >>
■91509  メモリリークに関して
□投稿者/ kiku -(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;
    }
親記事 /過去ログ158より / 関連記事表示
削除チェック/

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

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

    http://pro.art55.jp/?eid=1109086
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91516  Re[2]: メモリリークに関して
□投稿者/ kiku -(2019/07/03(Wed) 09:06:41)
    No91511 (PANG2 さん) に返信
    > ■No91509 (kiku さん) に返信
    >>質問1
    >> 本当にフォントやラベルが解放されないことを確認する方法はないでしょうか?
    >
    > WeakReferenceを List<WeakReference> に貯蔵して終了時に確認する。
    >
    > http://pro.art55.jp/?eid=1109086

    弱い参照を保持することが出来るんですね。
    ご案内頂きありがとうございます。
    試してみます。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91513  Re[1]: メモリリークに関して
□投稿者/ WebSurfer -(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 パターンによっ
    て、最悪でもファイナライザでアンマネージドリソースを開放できるようにします。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91517  Re[2]: メモリリークに関して
□投稿者/ kiku -(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される認識
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91519  Re[3]: メモリリークに関して
□投稿者/ WebSurfer -(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 版の場合はどうなってるでしょう?
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91521  Re[4]: メモリリークに関して
□投稿者/ kiku -(2019/07/03(Wed) 13:26:36)
    No91519 (WebSurfer さん) に返信
    > 質問者さんの CE 版の場合はどうなってるでしょう?
    
    回答になっているかわかりませんが、
    Formは下記のようにusingにて囲っているため、
    必ずフォームのDipposeが呼ばれる認識でいます。
    
                using (var form = new Test())
                {
                    form.ShowDialog();
                }
    
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91522  Re[5]: メモリリークに関して
□投稿者/ WebSurfer -(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 などの別のメソッドを使用する場合」が気になります。気にしすぎだろうとは思
    いますが。
    
    
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91526  Re[6]: メモリリークに関して
□投稿者/ kiku -(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が呼ばれたがどうかの確認がやっぱり必要になってきそうです。
    これが今うまくできずに困っています。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91525  Re[4]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 -(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
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91528  Re[5]: メモリリークに関して
□投稿者/ kiku -(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 機が無いので、エミュレーターでの動作ですけれども。

    貼って頂いたソースを実機で動かすことはできましたが
    その結果をどのように掲示板に報告するのが良いのか
    ソースの内容を理解できていないため、
    わかりませんでした。
    継承関係を同じように報告すべきだとは思うのですが、
    ごめんなさい。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91530  Re[6]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 -(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 される結果になるようです。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91534  Re[7]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 -(2019/07/04(Thu) 09:13:04)
    No91530 (魔界の仮面弁士) に追記
    >>あと、経験則的なところで言えば、.NET Compact Framework 環境の場合、
    >>オブジェクトの使用後は、参照変数に Nothing 代入を積極的に行った方が良いみたいです。
    > Nothing の代入有無で、本当に解放状況が変化するかどうかを確認してみました。


    すみません!
    C# の質問に、うっかり VB で回答してたことに、今朝になって気がつきました。

    # .NET 対応前、CE アプリを eVB3 で行ってきた期間が長すぎて、
    # CE 環境だと反射的に VB で書く癖が…。orz


    > 端末を再起動して、空きメモリが十分に確保されている状態において、
    > Button1 でモードレス フォームを表示させた後、
    > Button2 でそれを Close させています。
    > すると、Close 直後の ★の行の Nothing 代入がコメントアウトされていた場合、
    > Form2 および LabelEx が直ちに Dispose されないケースが確認できました。

    上記の「Close 後、直ちに Dispose されていない」現象ですが、
    改めて C# (CFx 3.5 SP1) でも実験してみたところ、上記の状況を再現させられませんでした。
    Close されたら、null を代入せずとも即座に Dispose されているようにみえる…。


    改めて、昨日の VB コードでも再検証してみましたが、
    昨日と比べて問題の再現率が大幅に低下していました。
    狙って再現させることは難しそうです。


    public partial class Form1 : Form
    {
      private Form2 f = null;
      public Form1()
      {
        InitializeComponent();
        DoubleClick += delegate { Close(); };
      }

      /// <summary>新規に Form2 を表示するコード</summary>
      private void button1_Click(object sender, EventArgs e)
      {
        f = new Form2();
        f.Show();
      }

      /// <summary>直前に表示された Form2 を閉じるコード</summary>
      private void button2_Click(object sender, EventArgs e)
      {
        if (f != null /* && !f.IsDisposed */)
        {
          f.Close();
          // f = null; // ★
        }
      }
    }

    // -=-=-=-=-=-=-=-=-=-=-=-=-

    public partial class Form2 : Form
    {
      private readonly Guid Id = Guid.Empty;
      public Form2()
      {
        Id = Guid.NewGuid();
        Trace.WriteLine(String.Format("==> {0} 生成 {1}", GetType().Name, Id));
        InitializeComponent();
        Controls.Add(new LabelEx());
        Text = Id.ToString();
        DoubleClick += delegate { Close(); };
        // Disposed += delegate { Trace.WriteLine(string.Format("<== {0} 破棄 {1}", GetType().Name, Id)); };
      }
      public sealed class LabelEx : Label
      {
        private readonly Guid Id;
        public LabelEx()
        {
          Id = Guid.NewGuid();
          Trace.WriteLine(String.Format("==> {0} 生成 {1}", GetType().Name, Id));
          Text = Id.ToString();
          //Disposed += delegate { Trace.WriteLine(string.Format("<== {0} 破棄 {1}", GetType().Name, Id)); };
        }
        protected override void Dispose(bool disposing)
        {
          Trace.WriteLine(string.Format("<== {0} 破棄 {1} ({2})", GetType().Name, Id, disposing));
          base.Dispose(disposing);
        }
      }
      protected override void Dispose(bool disposing)
      {
        Trace.WriteLine(string.Format("<== {0} 破棄 {1} ({2})", GetType().Name, Id, disposing));
        if (disposing && (components != null))
        {
          components.Dispose();
        }
        base.Dispose(disposing);
      }
    }
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91536  Re[8]: メモリリークに関して
□投稿者/ kiku -(2019/07/04(Thu) 09:33:19)
    No91534 (魔界の仮面弁士 さん) に返信
    > すみません!
    > C# の質問に、うっかり VB で回答してたことに、今朝になって気がつきました。

    ぜんぜん大丈夫です。
    VBでもC#でもそれほど大差ないですし。

    > 上記の「Close 後、直ちに Dispose されていない」現象ですが、
    > 改めて C# (CFx 3.5 SP1) でも実験してみたところ、上記の状況を再現させられませんでした。
    > Close されたら、null を代入せずとも即座に Dispose されているようにみえる…。

    なるほど、nullを代入せずとも良さそうということですね。
    ご紹介頂いたnullを設定して効果があったという記事もありますし、
    念のため、nullを代入してみるのも良いかも思いました。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91533  Re[7]: メモリリークに関して
□投稿者/ kiku -(2019/07/04(Thu) 09:08:37)
    No91530 (魔界の仮面弁士 さん) に返信
    > 一方 .NET Comapct Framwork の場合、ソースコードは提供されていませんが、
    > No91525 の実験結果から、Form.Dispose(bool) は特に存在しておらず、
    > 直接 Control.Dispose(bool) がそのまま呼ばれていることが確認できました。

    だとすると、
    子コントロールは明示的にDisposeしなくても良く、
    子コントロールが保持するFontなどのアンマネージリソースのみをDisposeすれば良いのでしょうか?

    > Nothing の代入有無で、本当に解放状況が変化するかどうかを確認してみました。
    > 端末を再起動して、空きメモリが十分に確保されている状態において、
    > Button1 でモードレス フォームを表示させた後、
    > Button2 でそれを Close させています。
    > すると、Close 直後の ★の行の Nothing 代入がコメントアウトされていた場合、
    > Form2 および LabelEx が直ちに Dispose されないケースが確認できました。
    > しかし Nothing 代入するようにした場合は、Button2 の時点ですぐに Dispose されるようになりました。
    > ただ、何度も実験を繰り返すと空きメモリが減ってくるようで、
    > その場合はどうやら GC が発動するらしく、Nothing 代入せずとも
    > Dispose される結果になるようです。

    そうすると、子コントロールは明示的にDisposeを行って、
    子コントロールにnull(今回C#なため)を設定し、
    子コントロールが保持するアンマネージリソースに対しても明示的にDisposeを行い、
    その参照もnullに設定するのが良さそうと理解しました。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91537  Re[8]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 -(2019/07/04(Thu) 09:59:49)
    No91533 (kiku さん) に返信
    > 子コントロールは明示的にDisposeしなくても良く、
    > 子コントロールが保持するFontなどのアンマネージリソースのみをDisposeすれば良いのでしょうか?
    
    .NET Framework の場合、Control.Font プロパティは Ambient なので、手出しすべきではありません。
    一つの Font インスタンスが、複数のコントロールで扱われる可能性があるためです。
    
    
    .NET Compact Framework の場合は、正直分かりません。
    各種処理が Microsoft.AGL.Forms 名前空間経由で、DllImport されてしまっていて、
    処理内容を追跡できそうにありません。
    Microsoft 有償サポートに問い合わせようにも、ライフサイクルが既に終了してしまっていますし。
    
    
    以下、Font プロパティが返すインスタンスに関する実験コード。
    
    var sb = new StringBuilder();
    var f1 = textBox1.Font;
    var f2 = f1;
    textBox1.Font = f1;
    var f3 = textBox1.Font;
    
    sb.AppendLine("--等価判定の実装状況--");
    sb.AppendLine(string.Format("f1==f2         :{0}", f1 == f2));
    sb.AppendLine(string.Format("f1.Eq(f2)      :{0}", f1.Equals(f2)));
    sb.AppendLine(string.Format("RefEq(f1, f2)  :{0}", ReferenceEquals(f1, f2)));
    sb.AppendLine("--Fontプロパティ--");
    sb.AppendLine(string.Format("f2==f3         :{0}", f2 == f3));
    sb.AppendLine(string.Format("f2.Eq(f3)      :{0}", f2.Equals(f3)));
    sb.AppendLine(string.Format("RefEq(f2, f3)  :{0}", ReferenceEquals(f2, f3)));
    
    textBox1.Text = sb.ToString();
    MessageBox.Show(sb.ToString());
    
    ====
    
    【.NET Compact Framework 3.5 SP1】
    --等価判定の実装状況--
    f1==f2         :True
    f1.Eq(f2)      :True
    RefEq(f1, f2)  :True
    --Fontプロパティ--
    f2==f3         :False
    f2.Eq(f3)      :True
    RefEq(f2, f3)  :False
    
    
    【.NET Framework 3.5 SP1】
    --等価判定の実装状況--
    f1==f2         :True
    f1.Eq(f2)      :True
    RefEq(f1, f2)  :True
    --Fontプロパティ--
    f2==f3         :True
    f2.Eq(f3)      :True
    RefEq(f2, f3)  :True
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91538  Re[9]: メモリリークに関して
□投稿者/ kiku -(2019/07/04(Thu) 10:46:45)
    No91537 (魔界の仮面弁士 さん) に返信
    > .NET Framework の場合、Control.Font プロパティは Ambient なので、手出しすべきではありません。
    > 一つの Font インスタンスが、複数のコントロールで扱われる可能性があるためです。
    > .NET Compact Framework の場合は、正直分かりません。
    > 各種処理が Microsoft.AGL.Forms 名前空間経由で、DllImport されてしまっていて、
    > 処理内容を追跡できそうにありません。
    > Microsoft 有償サポートに問い合わせようにも、ライフサイクルが既に終了してしまっていますし。

    AmbientとはフォームのFontを子コントロールが継承する機能であると理解しました。
    よって、フォームのFontと子コントロールのFontは同じインスタンスである場合がある。
    このような状況において、子コントロールのFontをDisposeしてしまうと、
    フォームのFontがどうなっちゃうの?ってことであると理解しました。

    フォームAのFontと、フォームBのFontが共用されることがなければ
    フォームAのDisposeのタイミングで、フォームAの子コントールのFontを
    Disposeするには、問題ないと考えても良いのでしょうか?
    ※よくわからないと記述があるので、わからないという回答かと思いますが(泣き)


    > 以下、Font プロパティが返すインスタンスに関する実験コード。

    この実験の結果をどのように理解していいか
    正直に言いますと良くわかりませんでした。
    FALSEになるってことは、textBox1.Font = f1;を実行したときに
    f1が設定されるのではなく、フォームのFontから継承される機能が働き、
    別のインスタンスになるのではという理解で現在はいます。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91539  Re[10]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 -(2019/07/04(Thu) 12:11:33)
    No91538 (kiku さん) に返信
    > AmbientとはフォームのFontを子コントロールが継承する機能であると理解しました。
    > よって、フォームのFontと子コントロールのFontは同じインスタンスである場合がある。

    アンビエントの具体例として:

    .NET Framework の場合、
    デザイン画面で Form に Label を貼って、
    その後で Form の Font を変更すると、
    Label の Font も連動して変更されます。

    このとき、デザイナのプロパティ グリッドを見てみると、
    Form の Font プロパティは変更されて太字表示されていますが、
    Label の Font は細字表示のままとなります。

    Label の Font を意図的に変更すれば、Label の Font プロパティも
    太字で表示されますが、この状態だと、Form の Font に連動しなくなります。


    ところが、.NET Compact Framework の場合、上記の動作が実装されていませんので、
    Compact Framework の実装がアンビエントになっている可能性は低いと予想しています。


    > この実験の結果をどのように理解していいか
    > 正直に言いますと良くわかりませんでした。

    .NET Comapct Framework の Font プロパティの getter は、
    アクセスするたびに、新しい Font インスタンスを
    生成する実装になっている可能性が高いことが伺えます。

    その根拠は、
     var f1 = textBox1.Font;
     var f2 = textBox1.Font;
    において、f1 と f2 が別のインスタンスとなったという点からです。
    (同じインスタンスを返す条件が存在する可能性を否定することは出来ませんが)


    また、No91525 のコードで「get_Font」メソッドの実装状況を調べたところ、
    Font プロパティは Control クラスで宣言されているのみで、
    継承先(Label や Form や ContainerControl 等)でオーバーライドされていないこともわかります。


    という事はすなわち、最初の No91509 にて記述されていた、

    >> this.lbl_serialnumber.Font.Dispose();//★対策追加

    というコードが無価値であるように思うのです。

    Font プロパティにアクセスするたびに新しい Font が生成され、
    それがすぐに破棄されるだけなのではないかと。
    (メモリ負荷等は見ていないので、アンマネージリソースの管理状況までは分かりませんが)


    では、そもそも破棄した Font を使おうとしたらどうなるのか?

    var f1 = new Font(FontFamily.GenericMonospace, 12, FontStyle.Regular);
    var f2 = new Font(FontFamily.GenericMonospace, 12, FontStyle.Regular);
    f2.Dispose();
    button1.Font = f1;
    button2.Font = f2;
    button1.Font.Dispose();



    てっきり ObjectDisposedException が発生するのかと思いきや、そうでは無いようです。

    .NET Compact Framework の場合は、button1 だけが新しいフォントになります。
    button2 の方は以前のフォントのままレンダリングされていました。

    一方 .NET Framework においては、button1 / button2 両方とも
    新しいフォントでレンダリングされました。エラーになることもなく。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91540  Re[11]: メモリリークに関して
□投稿者/ kiku -(2019/07/04(Thu) 13:13:14)
    No91539 (魔界の仮面弁士 さん) に返信
    > ところが、.NET Compact Framework の場合、上記の動作が実装されていませんので、
    > Compact Framework の実装がアンビエントになっている可能性は低いと予想しています。

    ありがとうございます。
    アンビエントに関して良く理解できました。

    > .NET Comapct Framework の Font プロパティの getter は、
    > アクセスするたびに、新しい Font インスタンスを
    > 生成する実装になっている可能性が高いことが伺えます。

    解説ありがとうございます。
    確かにgetterで新しいインスタンスを返すのであるならば
    意味ないと思います。

    textBox1.Font = f1;
    上記のsetterで新しいインスタンスが生成されるという
    ことは考えられないでしょうか?
    この考えでもつじつまがあうように思いました。


記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

■91541  Re[12]: メモリリークに関して
□投稿者/ 魔界の仮面弁士 -(2019/07/04(Thu) 15:18:05)
    2019/07/04(Thu) 15:29:28 編集(投稿者)

    No91540 (kiku さん) に返信
    > textBox1.Font = f1;
    > 上記のsetterで新しいインスタンスが生成されるという
    > ことは考えられないでしょうか?

    仮に setter でインスタンスが生成されていたとしても、
    それと別に、getter で毎回生成されていることは間違いないかと。


    using (var txt = new TextBox())
    {
     // CE 環境においては "違う" らしい
     MessageBox.Show(ReferenceEquals(txt.Font, txt.Font) ? "同じ" : "違う");
    }
    using (var txt = new TextBox { Font = null })
    {
     // "not null" と表示される
     MessageBox.Show((txt.Font == null) ? "null" : "not null");
    }



    No91539 (魔界の仮面弁士) に追記
    > var f1 = new Font(FontFamily.GenericMonospace, 12, FontStyle.Regular);
    > var f2 = new Font(FontFamily.GenericMonospace, 12, FontStyle.Regular);
    > f2.Dispose();
    > button1.Font = f1;
    > button2.Font = f2;
    > button1.Font.Dispose();
    >
    > .NET Compact Framework の場合は、button1 だけが新しいフォントになります。
    > button2 の方は以前のフォントのままレンダリングされていました。

    違ってました。

    デザイン時に別のフォントを指定してから実行してみると、
    button2 のフォントが変化する様子が確認できたので、
    button2 が「以前のフォントのまま」で変化しないという推察は誤りですね。

    Control.Font プロパティに「Dispose 済みの Font」や「null」が渡された場合、
    『既定のフォントがセットされる』という動作になっているようです。
記事No.91509 のレス /過去ログ158より / 関連記事表示
削除チェック/

次の20件>

<< 0 | 1 | 2 >>

パスワード/

- Child Tree -