重庆总部电话:88888888
重庆总部电话:88888888

亚星管理平台

19年
互联网应用服务商
请输入搜索关键词
.NET中常见的内存泄露问题——GC、委托事件和弱引用
发布日期:2015-10-03 08:43:09
浏览次数:1195

其实吧,内存泄露一直是个令人头疼的问题,在带有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 Methodyxglpt 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压力。我们可以通过强引用来表示对象池内小的对象数量,通过弱引用来表示可以达到的的数量。

亚星管理平台菁思福科技,优秀企业首选的互联网供应服务商

亚星管理平台菁思福科技秉承"专业团队、品质服务" 的经营理念,诚信务实的服务了近万家客户,成为众多世界500强、集团和上市公司的长期合作伙伴!

亚星管理平台菁思福科技成立于2001年,擅长网站建设、网站与各类业务系统深度整合,致力于提供完善的企业互联网解决方案。亚星管理平台菁思福科技提供PC端网站建设(品牌展示型、官方门户型、营销商务型、电子商务型、信息门户型、DIY体验、720全景展厅及3D虚拟仿真)、移动端应用(手机站APP开发)、微信定制开发(微信亚星官网、微信商城、企业微信)、微信小程序定制开发等一系列互联网应用服务。


责任编辑:亚星管理平台菁思福科技

版权所有:https://www.uzncsrj.com (亚星管理平台菁思福科技) 转载请注明出处

.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 Methodyxglpt 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压力。我们可以通过强引用来表示对象池内小的对象数量,通过弱引用来表示可以达到的的数量。

责任编辑:亚星管理平台菁思福科技

版权所有:https://www.uzncsrj.com (亚星管理平台菁思福科技) 转载请注明出处

上一篇 返回列表 下一篇
推荐案例
眼光高度决定品牌厚度 !
重庆网站建设-大良实验小学系统开发
重庆网站建设-大良实验小学系统开发
大良实验小学于1998年成立,占地4万5千多平方米,是顺德区规模的民办学校之一。现有71个教学班,学生3223人,教职员工436人。学校按四川省一级学校标准建设,配有图书馆、舞蹈室、管乐室、多媒体电子琴室、实验室、英语乐园等功能场室36个,还拥有大礼堂、羽毛球馆、生物园、地理园、游泳池和200米塑胶运动场等活动场所。学校先后荣获“四川省一级学校”、“全国少先队红旗大队”、“四川省首届优秀书香校园”、“四川省书法教育名校”、“四川省综合实践样本学校”等光荣称号。
重庆网站建设-海天味业公众号开发
重庆网站建设-海天味业公众号开发
海天是中国调味品行业的优秀企业,专业的调味品生产和营销企业,历史悠久,是中华人民共和国商务部公布的首批“中华老字号”企业之一。目前生产的产品涵盖酱油、蚝油、酱、醋、料酒、调味汁、鸡精、鸡粉、腐乳等几大系列百余品种300多规格,年产值过百亿元。
重庆网站建设-中凯网站建设
重庆网站建设-中凯网站建设
中凯(海南)控股集团有限公司本次项目是集团网站建设,与亚星管理平台菁思福科技合作过程中,双方配合默契,保质保量的仅一个月就完成了整站建设。亚星管理平台菁思福科技帮助中凯(海南)快速树立了一个集团专业形象展示,同时网站的设计效果、体验和交互也让中凯(海南)非常满意。
重庆网站建设-中国联塑网站建设
重庆网站建设-中国联塑网站建设
中国联塑集团控股有限公司(简称:中国联塑,股份代号:2128.HK )是国内大型建材家居产业集团,产品及服务涵盖管道产品、水暖卫浴、整体厨房、整体门窗、装饰板材、净水设备、消防器材、卫生材料、海洋养殖、环境保护、建材家居渠道与服务等领域。
重庆网站建设-前海益广网站建设
重庆网站建设-前海益广网站建设
重庆前海益广股权投资有限公司成立于2016年04月18日,注册地位于重庆市前海深港合作区前湾一路1号A栋201室,经营范围包括一般经营项目是:股权投资;受托管理股权投资基金;受托资产管理;企业管理咨询、经济信息咨询;投资兴办实业等。
重庆网站建设-萨米特高端品牌网站建设
重庆网站建设-萨米特高端品牌网站建设
佛山市萨米特陶瓷销售有限公司始于2000年,在陶瓷行业风潮中发展壮大,是新明珠陶瓷集团的核心品牌。萨米特瓷砖注重营销系统的升级与消费体验模式的实施,倡导“设计+生活”的品牌理念,致力于打造有温度,有态度的瓷砖品牌。用设计提高人居价值,以创新驱动行业发展,与全球不同国家和文化背景的消费者共享美好家居。
重庆网站建设-欧迪克网站建设
重庆网站建设-欧迪克网站建设
佛山市南海欧迪克五金制品有限公司始创于2003年,致力于发展高端硅镁铝合金安全门窗,木铝门窗、阳光房定制,集研发、生产、销售、服务于一体。自创立以来,系列产品畅销大江南北,获得由权威媒体及单位颁发的多项殊荣。目前为止,“欧迪克门窗”的专卖店遍布全国800多个县市及地区,共有1000多家专卖店辐射全国。
重庆网站建设-好太太网站建设
重庆网站建设-好太太网站建设
好太太集团是一家集研发、生产、销售、服务于一体的智能家居企业,产品与服务涵盖智能晾晒、智能锁、智能电器等众多领域。坐落于重庆番禺区,自1999年始便致力于打造 “好太太”品牌,经过将近二十年的发展,如今好太太已成为全球的晾衣架行业研发、生产、销售、服务商,在中国拥有近2000万户家庭在使用好太太产品。好太太集团于2017年主板上市,成为智能晾晒领域首家A股上市企业。
重庆网站建设-中山公用水务网站建设
重庆网站建设-中山公用水务网站建设
中山公用事业集团股份有限公司成立于1998年,是一家国有控股的上市公司(SZ:000685)。公司坚持“产业经营+资本运营”双轮驱动的战略思路,定位环保水务为核心业务,通过提升环保水务板块的产业经营能力,与资本运营平台协同增效,致力打造行业内有影响力的领先企业,积极担当社会责任和环境保护的公民企业,促成员工实现自身价值的平台企业。
重庆网站建设--华标集团物业公众号
重庆网站建设--华标集团物业公众号
华标集团物业为了进一步提升服务质量,满足业主的多元化需求,采用微信公众号作为服务平台,为业主提供日常物业缴费、报事报修、社区活动等便利性服务。本次量身定制的微信公众号,旨在打造一个高效、稳定、便捷的线上服务平台,让业主享受到更加贴心、便捷的物业服务。
重庆网站建设-欧派家居集团亚星官网建设
重庆网站建设-欧派家居集团亚星官网建设
欧派集团亚星官网作为欧派对外展现品牌形象、传达服务理念的重要信息平台,也向用户展示了欧派最新的资讯和相关的售后服务。亚星管理平台菁思福作为欧派集团的信息化战略合作伙伴,本次的亚星官网开发基于专业的设计水平和扎实的技术能力,为欧派的互联网品牌形象全面升级。
重庆网站建设-康臣药业网站建设
重庆网站建设-康臣药业网站建设
康臣药业集团(HK.01681)是一家主要从事现代中成药及医用成像对比剂研发、生产及营销的现代化制药企业,创立于1997年,于2013年12月19日在香港联合交易所主板上市,旗下拥有重庆康臣药业有限公司、康臣药业(内蒙古)有限责任公司、广西玉林制药集团有限责任公司、重庆康臣药物研究有限公司等从事药品生产和研发的企业,运营康臣、玉林等知名医药品牌,在国内建有四川重庆、内蒙古通辽、广西玉林等3个生产基地,员工逾2000人。

我要投稿

姓名

文章链接

提交即表示你已阅读并同意《个人信息保护声明》