亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#中event內存泄漏總結

2019-10-29 21:05:54
字體:
來源:轉載
供稿:網友

內存泄漏是指:當一塊內存被分配后,被丟棄,沒有任何實例指針指向這塊內存, 并且這塊內存不會被GC視為垃圾進行回收。這塊內存會一直存在,直到程序退出。C#是托管型代碼,其內存的分配和釋放都是由CLR負責,當一塊內存沒有任何實例引用時,GC會負責將其回收。既然沒有任何實例引用的內存會被GC回收,那么內存泄漏是如何發生的?

內存泄漏示例

為了演示內存泄漏是如何發生的,我們來看一段代碼

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事件,引發事件后,會在屏幕上看到 TestAction Run。當memory =null 后,memory原來指向的內存就沒有任何實例再引用該塊內存了,這樣的內存就是待回收的內存。GC.Collect(GC.MaxGeneration)語句會強制執行一次垃圾回收,再次引發事件,發現屏幕上還是會顯示TestAction Run。該內存沒有被GC回收,這就是內純泄漏。這是由TestEvent+=memory.Run語句引起的,當GC.Collect執行的時候,當他看到該塊內存還有TestEvent引用,就不會進行回收。但是該內存已經是“無法到達”的了,即無法調用該塊內存,只有在引發事件的時候,才能執行該內存的Run方法。這顯然不是我想要的效果,當memory = null執行時,我希望該內存在GC執行時被回收,并且當TestEvent被引發時,Run方法不會執行,因為我已經把該內存“解放”了。

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

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

IL_0000: nopIL_0001: newobj  instance void EventLeakMemory.Program/TestAction::.ctor()IL_0006: stloc.0IL_0007: ldloc.0IL_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)...//其他部分

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

解決辦法

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

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();}

當執行GC。Collect后,gcHandle.Target == null 由false 變成了true。這個gcHandle就是obj的一個弱引用。這個類的詳細介紹見 GCHandle 。比較關鍵的是GCHandle.Alloc方法的第二個參數,該參數接受一個枚舉類型。我使用的是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> /// 引用的目標是否還存活(沒有被GC回收) /// </summary> public bool IsAlive {  get {   if (handle == default(GCHandle)) return false;   return handle.Target != null;  } } /// <summary> /// 引用的目標 /// </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> /// 引發事件 /// </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#教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久国产一区二区三区| 性欧美xxxx视频在线观看| 国产亚洲精品美女久久久久| 亚洲精美色品网站| 亚洲一区二区国产| 久久久久久国产三级电影| 国产拍精品一二三| 91亚洲一区精品| 国产精品xxx视频| 91久久精品一区| 97国产精品免费视频| 国产精品久久久久7777婷婷| 久久久91精品国产| 国内精品久久久久伊人av| 亚洲福利视频网站| 国产999精品久久久| 国产69久久精品成人看| 日韩av男人的天堂| 国产精品久久久亚洲| 久操成人在线视频| 日本一区二区在线免费播放| 亚洲第一页自拍| 欧美日韩亚洲一区二区三区| 亚洲精品综合久久中文字幕| 北条麻妃一区二区在线观看| 精品国产乱码久久久久久婷婷| 中文字幕亚洲精品| 日韩精品高清在线| 日韩小视频网址| 久久精品在线视频| 国产精品视频专区| 最近更新的2019中文字幕| 一区二区三欧美| 欧美日韩亚洲精品一区二区三区| 欧美精品手机在线| 亚洲护士老师的毛茸茸最新章节| 2019日本中文字幕| 亚洲男人的天堂网站| 精品av在线播放| 欧美肥臀大乳一区二区免费视频| 亚洲国产成人精品久久| 另类少妇人与禽zozz0性伦| 欧美成人午夜视频| 亚洲国产91色在线| 国产视频欧美视频| 欧美激情一二区| 国产噜噜噜噜噜久久久久久久久| 国产精品久久久久77777| 欧美激情成人在线视频| 久久亚洲精品毛片| 国产精品91久久久| 国产精品视频久久久| 激情久久av一区av二区av三区| 欧美激情精品久久久久| 久久久精品一区二区三区| 国外成人免费在线播放| 91精品国产色综合久久不卡98口| 欧美日韩黄色大片| 日韩三级影视基地| 久久成人国产精品| 亚洲一区二区三区香蕉| 富二代精品短视频| 亚洲精品电影网在线观看| 日韩美女毛茸茸| 欧美日韩综合视频| 亚洲永久在线观看| 欧美人与性动交a欧美精品| 日韩视频精品在线| 日韩精品在线免费播放| 成人xvideos免费视频| 国产一区香蕉久久| 亚洲第一二三四五区| 日韩精品日韩在线观看| 亚洲男人的天堂在线| 亚洲人线精品午夜| 久久精品国产电影| 在线观看国产成人av片| 亚洲欧洲在线观看| 色悠久久久久综合先锋影音下载| 亚洲男人天堂2024| 精品日韩美女的视频高清| yw.139尤物在线精品视频| 国产精品网址在线| 日韩欧美中文字幕在线观看| 国产精品草莓在线免费观看| 亚洲综合中文字幕68页| 国产精品99蜜臀久久不卡二区| 九九精品在线观看| 91成人免费观看网站| 中文字幕精品www乱入免费视频| 日韩电影免费在线观看中文字幕| 国产在线观看91精品一区| 欧美理论片在线观看| 国产欧美精品va在线观看| 日韩视频精品在线| 91在线视频九色| 欧美成人免费播放| 久久久久久久久久久网站| 中文字幕欧美日韩| 亚洲男人的天堂在线播放| 色哟哟网站入口亚洲精品| 精品国产乱码久久久久酒店| 亚洲视频自拍偷拍| 日韩av最新在线| 精品夜色国产国偷在线| 午夜精品蜜臀一区二区三区免费| 亚洲色图激情小说| 久久久在线免费观看| 成人羞羞国产免费| 日韩欧美国产成人| 日本最新高清不卡中文字幕| 久久久人成影片一区二区三区| 久久在线精品视频| 亚洲一区二区三区四区视频| 欧美黑人极品猛少妇色xxxxx| 91久久精品国产91久久| 日韩在线观看视频免费| 亚洲日本aⅴ片在线观看香蕉| 91久久在线观看| 午夜精品久久久久久久久久久久久| 亚洲精品日韩激情在线电影| 国产精品久久一区| 欧美成人午夜激情视频| 色综合久久精品亚洲国产| 欧美资源在线观看| 91精品久久久久久久久久另类| 欧美亚洲第一区| 国产日韩中文字幕在线| 欧美日韩国产中文字幕| 国产一区二区三区日韩欧美| 91在线观看免费网站| 8x拔播拔播x8国产精品| 日韩欧美中文字幕在线播放| 国产精品一区二区三区久久| 亚洲精选中文字幕| 国产视频综合在线| 成人国产精品久久久久久亚洲| 国产精品久久久999| 欧美精品福利视频| 亚洲激情自拍图| 久久九九热免费视频| 日韩视频免费大全中文字幕| 中文字幕一区电影| 日韩电影大全免费观看2023年上| 精品国产欧美一区二区五十路| 91成人福利在线| 亚洲第一网站男人都懂| 一个人www欧美| 欧美日韩国产综合视频在线观看中文| 91在线免费视频| 午夜精品视频网站| 国产福利精品在线| 国产97在线亚洲| 不卡在线观看电视剧完整版| 在线激情影院一区| www欧美xxxx| 精品色蜜蜜精品视频在线观看| 欧美性猛交xxxx乱大交| 91久久国产精品91久久性色| 日韩av在线免播放器| 丰满岳妇乱一区二区三区| 亚洲第一综合天堂另类专| 91精品久久久久久久久久久久久| 91精品国产综合久久香蕉的用户体验|