成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

C#中event內(nèi)存泄漏的示例分析-創(chuàng)新互聯(lián)

小編給大家分享一下C#中event內(nèi)存泄漏的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

成都創(chuàng)新互聯(lián)公司一直秉承“誠信做人,踏實做事”的原則,不欺瞞客戶,是我們最起碼的底線! 以服務(wù)為基礎(chǔ),以質(zhì)量求生存,以技術(shù)求發(fā)展,成交一個客戶多一個朋友!為您提供成都網(wǎng)站建設(shè)、成都做網(wǎng)站、成都網(wǎng)頁設(shè)計、小程序開發(fā)、成都網(wǎng)站開發(fā)、成都網(wǎng)站制作、成都軟件開發(fā)、重慶APP開發(fā)是成都本地專業(yè)的網(wǎng)站建設(shè)和網(wǎng)站設(shè)計公司,等你一起來見證!

內(nèi)存泄漏示例

為了演示內(nèi)存泄漏是如何發(fā)生的,我們來看一段代碼

class Program 
{
 static event Action TestEvent;
 static void Main(string[] args)
 {
  var memory = new TestAction();
  TestEvent += memory.Run;
  OnTestEvent();
  memory = null;
  //強制垃圾回收
  GC.Collect(GC.MaxGeneration);
  Console.WriteLine("GC.Collect");
  //測試是否回收成功
  OnTestEvent();
  Console.ReadLine();
 }
 public static void OnTestEvent() {
  if (TestEvent != null) TestEvent();
  else Console.WriteLine("Test Event is null");
 }

 class TestAction 
 {
  public void Run() {
   Console.WriteLine("TestAction Run.");
  }
 }
}

該例子中,memory.run訂閱了TestEvent事件,引發(fā)事件后,會在屏幕上看到 TestAction Run。當(dāng)memory =null 后,memory原來指向的內(nèi)存就沒有任何實例再引用該塊內(nèi)存了,這樣的內(nèi)存就是待回收的內(nèi)存。GC.Collect(GC.MaxGeneration)語句會強制執(zhí)行一次垃圾回收,再次引發(fā)事件,發(fā)現(xiàn)屏幕上還是會顯示TestAction Run。該內(nèi)存沒有被GC回收,這就是內(nèi)純泄漏。這是由TestEvent+=memory.Run語句引起的,當(dāng)GC.Collect執(zhí)行的時候,當(dāng)他看到該塊內(nèi)存還有TestEvent引用,就不會進行回收。但是該內(nèi)存已經(jīng)是“無法到達”的了,即無法調(diào)用該塊內(nèi)存,只有在引發(fā)事件的時候,才能執(zhí)行該內(nèi)存的Run方法。這顯然不是我想要的效果,當(dāng)memory = null執(zhí)行時,我希望該內(nèi)存在GC執(zhí)行時被回收,并且當(dāng)TestEvent被引發(fā)時,Run方法不會執(zhí)行,因為我已經(jīng)把該內(nèi)存“解放”了。

這里有一個問題,就是C#中如何“釋放”一塊內(nèi)存。像C和C++這樣的語言,內(nèi)存的聲明和釋放都是開發(fā)人員負(fù)責(zé)的,一旦內(nèi)存new了出來,就要delete,不然就會造成內(nèi)存泄漏。這更靈活,也更麻煩,一不小心就會泄漏,忘記釋放、線程異常而沒有執(zhí)行釋放的代碼...有手動分配內(nèi)存的語言就有自動分配和釋放的語言。最開始使用垃圾回收的語言是LISP,之后被用在Java和C#等托管語言中。像C#,CLR負(fù)責(zé)內(nèi)存的釋放,當(dāng)程序執(zhí)行一段時間后,CLR檢測到垃圾內(nèi)存已經(jīng)值得進行一次垃圾回收時,會執(zhí)行垃圾回收。至于如何判定一塊內(nèi)存是否為垃圾內(nèi)存,比較著名的是計數(shù)法,即有一個實例引用了該內(nèi)存后,就在該內(nèi)存的計數(shù)上+1,改實例取消了對該內(nèi)存的引用,計數(shù)就-1,當(dāng)計數(shù)為0時,就被判定為垃圾。該種方法的問題是對循環(huán)引用束手無策,如A的某個字段引用了B,而B的某個字段引用了A,這樣A和B的技術(shù)都不會降到0。CLR改用的方法是類似“標(biāo)記引用法”(我自己的命名):在執(zhí)行GC時,會掛起全部線程,并將托管堆中所有的內(nèi)存都打上垃圾的標(biāo)記,之后遍歷所有可到達的實例,這些實例如果引用了托管堆的內(nèi)存,就將該內(nèi)存的標(biāo)記由垃圾變?yōu)楸灰谩.?dāng)遇到A和B相互引用的時候,如果沒有其他實例引用A或者B,雖然A和B相互引用,但是A和B都是不可到達的,即沒辦法引用A或者B,則A和B都會被判定為垃圾而被回收。講解了這么一大堆,目的就是要說,在C#中,你想要釋放一塊內(nèi)存,你只要讓該塊內(nèi)存沒有任何實例引用他,就可以了。那么當(dāng)執(zhí)行memory = null后,除了對TestEvent的訂閱,沒有任何實例再引用了該塊內(nèi)存,那么為什么訂閱事件會阻止內(nèi)存的釋放?

我們來看看TestEvent+=memory.Run()這句話都干了什么。我們利用IL反編譯上面的dll,可以看到

IL_0000: nop
IL_0001: newobj  instance void EventLeakMemory.Program/TestAction::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldftn  instance void EventLeakMemory.Program/TestAction::Run()
IL_000e: newobj  instance void [mscorlib]System.Action::.ctor(object, native int)
IL_0013: call  void EventLeakMemory.Program::add_TestEvent(class [mscorlib]System.Action)...//其他部分

關(guān)鍵在5-7行。第5和6行,聲明了一個System.Action型的委托,參數(shù)為TestAction.Run方法,第七行,執(zhí)行了Program.add_TestEvent方法,參數(shù)是上面聲明的委托。也就是說+=操作符相當(dāng)于執(zhí)行了Add_TestEvent(new Action(memory.Run)),就是這個new Action包含了對memory指向的內(nèi)存的引用。而這個引用在CLR看來是可達的,可以通過引發(fā)事件來調(diào)用該內(nèi)存。

解決辦法

我們已經(jīng)找到了內(nèi)存泄漏的元兇,就是訂閱事件時,隱式聲明的匿名委托對內(nèi)存的引用。該問題的解決辦法是使用一種和普通的引用不同的方式來引用方法的實例對象:該引用不會影響垃圾回收,不會在GC時被判定為對該內(nèi)存的引用,也就是“弱引用”。C#中,絕大部分的類型都是強引用。如何實現(xiàn)弱引用?來看一個例子:

static void Main(string[] args){
 var obj = new object();
 var gcHandle = GCHandle.Alloc(obj, GCHandleType.Weak);
 Console.WriteLine("gcHandle.Target == null is :{0}", gcHandle.Target == null);
 obj = null;
 GC.Collect();
 Console.WriteLine("GC.Collect");
 Console.WriteLine("gcHandle.Target == null is :{0}", gcHandle.Target == null);
 Console.ReadLine();
}

當(dāng)執(zhí)行GC。Collect后,gcHandle.Target == null 由false 變成了true。這個gcHandle就是obj的一個弱引用。這個類的詳細(xì)介紹見 GCHandle 。比較關(guān)鍵的是GCHandle.Alloc方法的第二個參數(shù),該參數(shù)接受一個枚舉類型。我使用的是GCHandleType.Weak,表明該引用是個弱引用。利用這個方法,就可以封裝一個自己的WeakReference類,代碼如下

public class WeakReference<T> where T : class {
 private GCHandle handle;

 public WeakReference(T obj) {
  if (obj == null) return;
  handle = GCHandle.Alloc(obj, GCHandleType.Weak);
 }

 /// <summary>
 /// 引用的目標(biāo)是否還存活(沒有被GC回收)
 /// </summary>
 public bool IsAlive {
  get {
   if (handle == default(GCHandle)) return false;
   return handle.Target != null;
  }
 }

 /// <summary>
 /// 引用的目標(biāo)
 /// </summary>
 public T Target {
  get {
   if (handle == default(GCHandle)) return null;
   return (T)handle.Target;
  }
 }
}

利用該類,就可以寫一個自己的弱事件封裝器。

public class WeakEventManager<T> {
 private Dictionary<Delegate, WeakReference<T>> delegateDictionary;

 public WeakEventManager() {
  delegateDictionary = new Dictionary<Delegate, WeakReference<T>>();
 }

 /// <summary>
 /// 訂閱
 /// </summary>
 public void AddHandler(Delegate handler) {
  if (handler != null)
   delegateDictionary[handler] = new WeakReference<T>(handler);
 }

 /// <summary>
 /// 取消訂閱
 /// </summary>
 public void RemoveHandler(Delegate handler) {
  if (handler != null)
   delegateDictionary.Remove(handler);
 }

 /// <summary>
 /// 引發(fā)事件
 /// </summary>
 public void Raise(object sender, EventArgs e) {
  foreach (var key in delegateDictionary.Keys) {
   if (delegateDictionary[key].IsAlive)
    key.DynamicInvoke(sender, e);
   else
    delegateDictionary.Remove(key);
  }
 }
}

最后,就可以像下面這樣定義自己的事件了

public class TestEventClass {
 private WeakEventManager<Action<object, EventArgs>> _testEvent = new WeakEventManager<Action<object, EventArgs>>();
 public event Action<object, EventArgs> TestEvent {
  add { _testEvent.AddHandler(value); }
  remove { _testEvent.RemoveHandler(value); }
 }

 protected virtual void OnEvent(EventArgs e) {
  _testEvent.Raise(this, e);
 }
}

以上是“C#中event內(nèi)存泄漏的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)成都做網(wǎng)站行業(yè)資訊頻道!

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。

網(wǎng)站欄目:C#中event內(nèi)存泄漏的示例分析-創(chuàng)新互聯(lián)
本文地址:http://jinyejixie.com/article44/djcshe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護、App開發(fā)、軟件開發(fā)、品牌網(wǎng)站制作、響應(yīng)式網(wǎng)站、網(wǎng)站策劃

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)