一、不手動註銷事件也不發生內存泄露的狀況html
咱們常常會寫EventHandler += AFunction; 若是沒有手動註銷這個Event handler相似:EventHandler –= AFunction 有可能會發生內存泄露。post
public class Program { static void ShowMemory() { Console.WriteLine("共用內存:{0}M", GC.GetTotalMemory(true) / 1024 / 1024); } static void Main(string[] args) { ShowMemory(); for (int i = 0; i < 5; i++) { EventSample es = new EventSample(); es.ShowComplete += es.MyEventHandler; GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); ShowMemory(); } Console.ReadKey(); } } public class EventSample { byte[] m_ExtraMemory = new byte[1024 * 1024 * 12]; //定義一個事件 public event EventHandler ShowComplete; //觸發事件 public void OnShowComplete() { //判斷是否綁定了事件處理方法,null表示沒有事件處理方法 if (ShowComplete != null) { //像調用方法同樣觸發事件 ShowComplete(this, new EventArgs()); } } //事件處理方法 public void MyEventHandler(object sender, EventArgs e) { Console.WriteLine("誰觸發了我?" + sender.ToString()); } }
上述代碼輸出以下:this
從輸出來看,內存被GC正常地回收,沒有問題。spa
二、內存泄露的狀況3d
咱們來將代碼改動一下code
public class Program { static void ShowMemory() { Console.WriteLine("共用內存:{0}M", GC.GetTotalMemory(true) / 1024 / 1024); } static void Main(string[] args) { ShowMemory(); for (int i = 0; i < 5; i++) { Microsoft.Win32.SystemEvents.DisplaySettingsChanged += new EventHandler(new MyMethod().SystemEvents_DisplaySettingsChanged); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); ShowMemory(); } Console.ReadKey(); } } public class MyMethod { byte[] m_ExtraMemory = new byte[1024 * 1024 * 12]; public void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e){ } }
輸出結果以下:htm
從輸出結果來看,內存已不能被GC正常回收。爲何會出現這種狀況呢?咱們來看看Microsoft.Win32.SystemEvents.DisplaySettingsChanged的源代碼(省略先後部分):對象
public sealed class SystemEvents { ... ... public static event EventHandler DisplaySettingsChanged ... ... }
爲何會有差異,根本區別在於後者有個SystemEvents.DisplaySettingsChanged事件,而這個事件是靜態的。blog
三、釋放資源生命週期
若是咱們但願釋放資源,則咱們須要在某個地方實現-=AFunction操做
public class Program { static void ShowMemory() { Console.WriteLine("共用內存:{0}M", GC.GetTotalMemory(true) / 1024 / 1024); } static void Main(string[] args) { ShowMemory(); for (int i = 0; i < 5; i++) { using (MyMethod myMethod = new MyMethod()) { Microsoft.Win32.SystemEvents.DisplaySettingsChanged += new EventHandler(myMethod.SystemEvents_DisplaySettingsChanged); } GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); ShowMemory(); } Console.ReadKey(); } } public class MyMethod : IDisposable { byte[] m_ExtraMemory = new byte[1024 * 1024 * 12]; public void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) { } public void Dispose() { Microsoft.Win32.SystemEvents.DisplaySettingsChanged -= new EventHandler(SystemEvents_DisplaySettingsChanged); } }
輸出以下:
增長了一個Dispose來實現 "-="功能就OK了。
靜態對象生命週期很長,永遠不會被GC回收,一旦被他給引用上了,那就不可能釋放了。上面的例子就是被靜態的DisplaySettingsChanged 引用致使不能被回收。
另一個要注意的是Singleton單例模式實現的類,他們也是static的生命週期很長,要注意引用鏈,你的類是否被它引用上,若是在它的引用鏈上,就內存泄露了。
參考:http://www.cnblogs.com/Mainz/archive/2011/09/10/2173162.html
http://www.cnblogs.com/kissdodog/p/3672460.html
http://www.cnblogs.com/artech/archive/2010/10/18/CLR_Memory_Mgt_01.html
http://www.cnblogs.com/artech/archive/2010/10/20/CLR_Memory_Mgt_02.html