广州总部电话:020-85564311

TOP

收起快捷

首页 > 关于途必 > 途必学院 > .NET中常见的内存泄露问题——GC、委托事件和弱引用
.NET中常见的内存泄露问题——GC、委托事件和弱引用
发布日期:2015-10-03 08:43:09
浏览次数:
来源:广州网站建设

其实吧,内存泄露一直是个令人头疼的问题,在带有GC的语言中这个情况得到了很大的好转,但是仍然可能会有问题。

一、什么是内存泄露(memory leak)?

内存泄露不是指内存坏了,也不是指内存没插稳漏出来了,简单来说,内存泄露就是在你期待的时间内你程序所占用的内存没有按照你想象中的那样被释放。

因此什么是你期待的时间呢?明白这点很重要。如果一个对象占用内存的时间和包含这个对象的程序一样长,但是你并不期望是这样。那么就可以认为是内存泄露了。用具体例子来说明如下:

class Button {

  public void OnClick(object sender, EventArgs e) {

    ...

  }

}class Program {

  static event EventHandler ButtonClick;

  static void Main(string[] args) {

      Button button = new Button();

      ButtonClick += button.OnClick;   

  }

}

上面这段代码中,我们使用了一个静态的事件,而静态成员的生命周期是从AppDomain被加载开始,直到AppDomain被卸载,也就是说在通常情况下如果进程没被关闭,又忘记取消注册事件,那么ButtonClick事件包含的EventHandler委托所引用的对象会一直存在到进程结束为止,这就造成了内存泄露问题。这也是.NET中常见的内存泄露问题的原因之一。后面我会接着说怎么解决这种事件造成的泄露问题。

二、内存回收的方式

1、引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个 变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办 法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。

像原来IE6中Javascript中原生对象内存回收的方式就是通过检查对象是否有引用来判断一个对象是否是垃圾。IE9之前,其BOM和DOM中的对象是使用C++以COM对象的形式实现的,而COM对象的垃圾收集机制采用的也是引用计数策略。而这种方式通常会因为循环引用导致内存泄露,也就是A引用B的同时,B也引用者A。 在Objective-C中也会有这样的循环引用的问题。在Objective-C中的解决方案就是给一方标记为weak,介绍可以参看这里,关于Objective-C中的委托模式的介绍。

2、标记清除法(mark-weep)

C#中采用的是标记法回收内存,全部对象都要标记,并且只标记一次就不再标记。判断一个对象是不是垃圾取决于是否有引用,而是取决是是否被root引用。

root的类型有寄存器中的变量,线程栈上的变量,静态变量等。

我们来看一幅通常情况下的对象图,图中有一个循环引用。

 

我们抽取其中一部分图说明

在采用标记清除策略的实现中,由于函数执行之后,local3出栈,离开了作用域,因此这种相互引用在标记清除法中不是个问题。

 

我们很容易看出,因为每一个对象都要mark,因此创建大量的小对象会给Mark阶段造成压力。值得注意的是,在GC的mark和weep阶段,会挂起所有线程,因此创建大量的线程也是会对GC造成问题。这个问题我以后会再讨论。

三、弱引用解决一些问题

如前面所说,忘记取消注册事件通常是.NET中常见的内存泄露问题,我们怎么自动化的解决这个问题呢?也就是说当方法所属的对象已经被标记为垃圾的时候,我们就在事件中取消注册这个方法。这时就可以通过弱引用来实现。

委托的本质就是一个类,包含了几个关键属性:

1. 指向原对象的Target属性(强引用)。

2. 一个指向方法的ptr指针。

3. 内部维护着一个集合(delegate是以链表结构实现)。

因为.NET中的委托是强引用,我们要把它改成弱引用,我们可以抓住这个这些特征,创建一个自己的WeakDelegate类。

事件的本质就是一个访问器方法,和委托的关系类似于字段和属性,也就是控制外部对字段的访问。我们可以通过自定义add和remove方法来把外部的委托转换成我们自己定义的委托。

public class Button

{

    private class WeakDelegate

    {

        public WeakReference Target;

        public MethodInfo Method;

    }

    private List<WeakDelegate> clickSubscribers = new List<WeakDelegate>();

    public event EventHandler Click

    {

        add

        {

            clickSubscribers.Add(new WeakDelegate

            {

                Target = new WeakReference(value.Target),

                Method = value.Method

            });

        }

        remove

{

          .....

       }

    }

    public void FireClick()

    {

        List<WeakDelegate> toRemove = new List<WeakDelegate>();

        foreach (WeakDelegate subscriber in clickSubscribers)

        {

//个Target表示方法所属的对象,第二个Target表示这个对象是否被标记为垃圾,如果为null则表示为已经被标记为垃圾。

            object target = subscriber.Target.Target;

            if (target == null)

            {

                toRemove.Add(subscriber);

            }

            else

            {

                subscriber.Method.Invoke(target, new object[] { this, EventArgs.Empty });

            }

        }

        clickSubscribers.RemoveAll(toRemove);

    }

}

弱引用还可以用来创建一个对象池,对象池就是通过管理少量的对象来减少内存和GC压力。我们可以通过强引用来表示对象池内小的对象数量,通过弱引用来表示可以达到的的数量。

责任编辑:途必技术部

版权所有:http://www.uweb.net.cn (优网科技) 转载请注明出处

关闭
推荐新闻
童心无界 童萌有礼——六一儿童节活动来啦!
童心无界 童萌有礼——六一儿童节活动来啦!
六一儿童节是属于“孩子们”节日,不管是天真卡爱的小朋友,还是童心未泯的大朋友。跟着我们一起重回童年,让童心出发吧。
2024.06.03 10:56:04
途必科技十一月生日会--关怀与爱
途必科技十一月生日会--关怀与爱
时隔一个月,又迎来了我们可爱小伙伴们的生日,本月有好几位的寿星生日。途必科技为了关怀员工,特地举办了此次的集体生日会活动,并且还有很多丰富的小吃零食,饮品饮料,当然更重要的蛋糕也是不可或缺的。
2023.11.29 10:57:26
一起成长---十月途必科技员工生日会
一起成长---十月途必科技员工生日会
秋高气爽的季节里头,我们迎来了十月份途必科技小伙伴们的生日,在这个温馨的聚会里一同感受着生日的快乐,能在工作繁忙之余享受到节日带来的温暖。每一次的生日会都是大家在共同的成长,当然啦,成长的路上少不了磕磕碰碰,但是在我们的人生里头,总会相遇温暖,陪伴左右,成长也就变得更加有意义。
2023.10.30 09:29:30
温暖与你同在-八月途必科技小伙伴生日会
温暖与你同在-八月途必科技小伙伴生日会
在每个人成长的背后,总会有温暖相伴,像在途必科技的大家庭里,大家都可以感受到这样的氛围,又温馨,有爱。也许有时候自己都忘了的生日,却是有人记得你的生日,有人愿意为你祝福,这又何尝不是一种温暖呢。途必科技与大家一同成长,也将爱送给大家,途必科技对出席生日会的每一位小可爱表示热烈的欢迎和衷心的感谢,同时也为八月份生日的寿星们送上美丽的祝福~祝愿可爱的寿星们天天开心,心想事成,身体健康,另外也希望每位途必科技的小伙伴在未来的工作中越来越顺利,生活里也越来越好。
2023.08.30 10:32:55
这次圣诞节,我们玩大的
这次圣诞节,我们玩大的
这次的圣诞节,我们玩大的,为了让大家感受到如火热情般的圣诞节,此次带来了很多不一样的玩法和活动。行政人事部代表途必科技给大家制造了很多不一样的惊喜,在这里你可以收获丰富满满的礼物,漂亮小姐姐和可爱圣诞老人的合影,小伙伴们其乐融融的氛围,
2022.12.25 17:00:04
推荐案例
眼光高度决定品牌厚度!
业务展示
专业团队,品质服务

预算不足没关系,

这里

广州市途必企业管理中心(有限合伙)版权所有 粤ICP备2020085998号 Copyright © 2023 All Rights Reserved
 
扫码咨询

专属顾问
扫码咨询您的优网专属顾问!