内存泄漏之Static Event

静态类里面的事件,再普通不过,经常使用它来做全局广播。
但是如果一不小心就会发生内存泄漏,请看下面的Demo:
我创建了一个简单的窗口Example4.xaml:

<Window x:Class="MemoryLeak.Example.Example4"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Window>

Example4.xaml.cs中的代码如下:

public partial class Example4 : Window
{
    //这里产生一个大的内存占用,约50MB,用于在任务管理器看到这个窗口Show出来以后,进程内存占用剧增的现象
    private readonly List<string> _bigList = ExampleHelper.BigList();
 
    public Example4()
    {
        InitializeComponent();

        ExampleHelper.LeakEvent += ExampleHelper_LeakEvent;
    }

    private void ExampleHelper_LeakEvent(object sender, System.EventArgs e)
    {
        Console.WriteLine(DateTime.Now);
    }
}

public static class ExampleHelper
{
    public static event EventHandler LeakEvent;
}

内存泄漏现象

Example4 window = new Example4();
window.Show();

然后将此window直接关闭,那么显然这个window生命周期结束,再无引用。执行:

GC.Collect();

window占用的50MB内存应该被回收,然后在任务管理器中看此进程,其内存并没有被回收。什么东西阻止了我回收?阻止回收的根本原因是仍然有人引用!我们来挖一下,深层次的原因在哪里。

源码分析

window生命周期分析

我们仔细看一下这个代码ExampleHelper.LeakEvent += ExampleHelper_LeakEvent;,这句代码的完整形式应该是ExampleHelper.LeakEvent += this.ExampleHelper_LeakEvent;我们知道事件的工作原理是观察者模式,要实现观察者模式就必须有目标Target和处理函数Method,那么ExampleHelper.LeakEvent += ExampleHelper_LeakEvent;这句话将this作为观察者模式中的Target,ExampleHelper_LeakEvent作为Method,当事件发生的时候可以通过Target调用Target中的Method,所以ExampleHelper.LeakEvent += ExampleHelper_LeakEvent;这句话就将window强引用了。而这个事件是个static变量哦,什么是staticstatic对象就是生命周期跟进程的生命周期同样长的对象。

image.png

内存泄漏原因深度剖析及解决措施

上述问题谈到ExampleHelper.LeakEvent += ExampleHelper_LeakEvent;,这句代码造成了对window的强引用,所以你不用的的时候需要手动解除一下引用关系:ExampleHelper.LeakEvent -= ExampleHelper_LeakEvent;,这样就可以回收了。
但是:
如果我们换一种办法:ExampleHelper.LeakEvent += (s, e) => { Console.WriteLine(DateTime.Now); };,这样会不会造成window无法被回收呢?答案是不回,但是如果是这样:ExampleHelper.LeakEvent += (s, e) => { Console.WriteLine(this.Width); };,那么window无法被回收。匿名函数到底发生了什么?为什么有时会阻止回收,有时不会?请看我的另一篇文章:《原来是这样:C#中的匿名函数 & 闭包(未完成)》

至此static event 造成的内存泄漏分析完毕,我们知晓了造成内存泄漏的根本原因就是还有引用,解决措施就很简单了:在不需要的时候解除引用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 事件是我们在WPF开发过程中使用的非常多的技术,但是如果一不小心就会发生内存泄漏,请看下面的Demo。我创建了一个...
    古意昌阅读 359评论 0 0
  • 我们经常会在程序中使用DispatcherTimer,但是如果一不小心就会发生内存泄漏,请看下面的Demo: 内存...
    古意昌阅读 3,442评论 1 2
  • 今天退了个麻麻群,年初为了备孕加的。 一个刚怀孕没多久的妹纸,叫她A好了,一大早在群里吐槽。 基本上就是刷弹幕的节...
    LubQ阅读 495评论 0 1
  • 曾经年少过、无知过,现在我还在路上。今天是12月25日-圣诞节,也意味着考研真正结束的日子。回首过往的备考时光,...
    飞飞哒313520阅读 212评论 0 0
  • 冯唐笔下的秋水,流里流气,细腻又简单粗暴。 但张一山版的秋水,满足了几乎所有我对另一半的幻想。浪漫又矫情、多情又专...
    尔青筝阅读 806评论 4 7