WeakPropertyChangedListener弱参照イベント購読によるメモリ漏洩防止

6177 ワード

GC対象回収メカニズム
1つのオブジェクトが回収されるかどうかは、主にゴミ回収器が参照している間にこの参照にアクセスできるかどうか、アクセスできない場合は回収され、アクセスできる場合は回収できません.つまりGCが回収される前にGCはオブジェクトに何の参照もないと判断します.GCによる「無用」オブジェクトの識別メカニズムは、オブジェクトが「Root(Root)」によって参照されているか否かを判断することである.ここで、「ルート」は、現在使用されている、または後で使用される可能性のあるオブジェクトのセットの総称であり、概ね、タイプの静的フィールドや、以前のメソッドパラメータやローカル変数、CPUレジスタなどを含む.
メモリ漏洩なしでオブジェクトが解放されたことを確認する方法
ANTS Memory Profiler(Redgate)というツールを使用して、プログラムを実行し、Formを開き、スナップショットを撮ります.そのFormを閉じて、もう1枚のスナップショットを撮って、2枚のスナップショットを比較して、Formが解放されたことを見てみましょうか?解放されていない場合は、誰に引用されてGCが解放されないかを見てみましょう.ANTS Memory Profilerの強みは、このオブジェクトが他のオブジェクトに参照されていることをよく見ることができることです.Redgateは、解放されていないオブジェクトがEventHandlerのイベント参照タイプであるかどうかに強く注目することをお勧めします.これはよくあるメモリ漏洩です.上の例の解決策も簡単で、Formが閉じたイベントにEventHandler-=...事件の傍受を解放すればいい.EventHandlerの本質はDelegateであり,それによってイベントListenerを参照者とした.EventHandler+=...この言葉は2つのオブジェクトを結びつけ,それによってイベントListenerを参照者とし,つまり1つのオブジェクトが別のオブジェクトに強く参照され,当然GCによって解放されない.もう一つの解決策は弱い参照-WeakReferenceです.もちろん、強いリファレンスに比べてパフォーマンスが低下します.
 
 
弱参照-WeakReference
以上は強引用です.GCは,強引をチェックすることにより,1つのオブジェクトが回収可能であるか否かを決定する.弱いリファレンスと呼ばれるリファレンスもあります(WeakReference)この参照はGC回収に影響しません.これがその用途です.たとえば、大きなStatic変数をキャッシュしている場合、Static変数は回収されません.この場合、この大きなオブジェクトの弱い参照を作成することができます.これにより、メモリが足りない場合にGCは回収でき、メモリの使用に影響しません.GC回収されない前にこのオブジェクトを二次利用する.
 
IPropertyChangedとメモリの漏洩
 
IPropertyChangedこのインタフェースはよく、client(例えば、バインドされたインタフェース)に通知するために一般的に使用され、例えば、実装された
IPropertyChangedインタフェースのPersonクラスは、その属性IsBusyの値が変化すると自動的にListenerに通知する.例えば、このビューモデルPersonクラスにビューがバインドされ、そのうちの1つのProgressBarコントロールがこのIsBusy属性にバインドされ、1つのConverterでboolの値をProgressBarの状態に変換する.これにより、ビューとモデルのバインドとイベントリスニングによって自動更新通知が実現されます.しかし、悲しいことに、プロセス全体のイベントはメモリの漏洩を検出し、具体的な原因は上のGC原理分析を参照してください.解決策は自分で一つ実現することだ.
メモリの漏洩がない
 
 
WeakPropertyChangedListener弱参照イベント購読によるメモリ漏洩防止
WeakPropertyChangedListener:コアインプリメンテーションは、WeakReference弱リファレンスを使用して、イベントリスナーの親が回収するときにメモリを回収することです.
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.ComponentModel;

namespace ConsoleApplication1

{

   public class WeakPropertyChangedListener

        {

            #region Private Fields

            private INotifyPropertyChanged _source;

            public WeakReference _listener;

            #endregion

            #region Ctor

            public WeakPropertyChangedListener(INotifyPropertyChanged source, PropertyChangedEventHandler listener)

            {

                this._source = source;

                this._source.PropertyChanged += this.OnPropertyChanged;

                this._listener = new WeakReference(listener);

            }

            #endregion

            #region Public Methods

            public static WeakPropertyChangedListener Create(INotifyPropertyChanged source, PropertyChangedEventHandler listener)

            {

                return new WeakPropertyChangedListener(source, listener);

            }

            public void Disconnect()

            {

                if (_source != null)

                {

                    _source.PropertyChanged -= this.OnPropertyChanged;

                    _source = null;

                    _listener = null;

                }

            }

            #endregion

            #region Private Methods

            private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)

            {

                if (_listener != null)

                {

                    var handler = _listener.Target as PropertyChangedEventHandler;

                    if (handler != null)

                    {

                        handler(sender, e);

                    }

                    else

                    {

                        this.Disconnect();

                    }

                }

            }

            #endregion

        }

}


INotifyPropertyChanged実装クラス
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.ComponentModel;

namespace ConsoleApplication1

{

    public class TestViewModel : INotifyPropertyChanged

    {

        private int _Value;

        public int Value

        {

            get

            {

                return _Value;

            }

            set

            {

                if (value != _Value)

                {

                    _Value = value;

                    NotifyPropertyChanged("Value");

                }

            }

        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        } 

    }

}


Unit testingのテスト
[TestMethod()]

        public void CreateTest()

        {

            TestViewModel source = new TestViewModel();

            PropertyChangedEventHandler listener = OnProperChangedHandler;

            WeakPropertyChangedListener actual = WeakPropertyChangedListener.Create(source, listener);

            bool fired = false;

            source.PropertyChanged += (s, a) =>

            {

                if (a.PropertyName == "Value")

                    fired = true;

            };

            source.Value = 5;

            Assert.IsTrue(fired);

            listener = null;//    

            GC.Collect(); //    

            source.Value = 6;

            Assert.IsNull(actual._listener); //   _listen public    

        }

        public void OnProperChangedHandler(object sender, PropertyChangedEventArgs args)

        {

        }


 
ダウンロード