zoukankan      html  css  js  c++  java
  • .NET内存泄漏(之 静态事件)

    一、事件引起的内存泄露

      1、不手动注销事件也不发生内存泄露的情况

      我们经常会写EventHandler += AFunction; 如果没有手动注销这个Event handler类似:EventHandler –= 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++)
                {
                    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());
            }
        }
    复制代码

      上述代码输出如下:

       

      从输出来看,内存被GC正常地回收,没有问题。

      2、内存泄露的情况

      我们来将代码改动一下

    复制代码
        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){ }
        }
    复制代码

      输出结果如下:

      

      从输出结果来看,内存已不能被GC正常回收。为什么会出现这种情况呢?我们来看看Microsoft.Win32.SystemEvents.DisplaySettingsChanged的源代码(省略前后部分):

        public sealed class SystemEvents
        {
            ... ...
            public static event EventHandler DisplaySettingsChanged
            ... ...
        }

      为什么会有差别,根本区别在于后者有个SystemEvents.DisplaySettingsChanged事件,而这个事件是静态的。

      3、释放资源

      如果我们希望释放资源,则我们需要在某个地方实现-=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

  • 相关阅读:
    C#编写ActiveX在WEB中应用
    Asp.net中Array的用法
    CAB打包方法
    C#获取文件路径的几种方法
    使用MakeCAB.exe命令创建CAB文件
    VS2005 工具里添加“OLE/COM 对象查看器”命令
    【转载】ASP.NET MVC中Controller与View之间的数据传递总结
    F如何配置外向交货单的定价过程
    解决 局域网XP无权限访问的问题 (转)
    U盘装XP系统(含截图,2012最新原创超简单方法)
  • 原文地址:https://www.cnblogs.com/jx270/p/7552122.html
Copyright © 2011-2022 走看看