分享.net常见的内存泄露及解决方法
分享.net常见的内存泄露及解决方法
关于内存泄漏的问题,之前也为大家介绍过,比如:《C++中内存泄漏的检测方法介绍》,是关于C++内存泄漏的。今天为大家介绍的是关于.NET内存泄漏的问题。
前段时间帮项目组内做了一次内存优化,产品是使用c#开发的winForm程序,一直以为.net提供了垃圾收集机制,开发的时候也没怎么注意内存的释放,导致最后的产品做出来之后,运行一个多小时就内存直接崩溃了,看来.net的垃圾收集还是得需要开发者加以控制,也不是万能的啊。
下面将对垃圾收集做以简介,然后描述一下我在内存优化过程中常见的内存泄露及解决方法。
托管堆的内存分配(下文中的托管堆指的是GC堆)
托管堆是以应用程序域为依托的,即每一个应用程序域有一个托管堆,每一个托管堆也只属于一个应用程序域,且托管堆是一块连续的内存,其中的对象也是紧密排列的。相对于C++中的非连续内存堆来说,托管堆的内存分配效率要高。托管堆维护了一个指针,指向当前已使用内存的末尾,当需要分配内存的时候,只需要指针向后移动指定数量的位置即可。而且托管堆通过应用程序域实现了应用程序之间内存的隔离,即不同的应用程序域之间在正常情况下是不能相互访问各自的托管堆的。
垃圾收集
垃圾收集的算法有很多。例如引用计数、标记清除等等,托管堆使用的标记清除算法。
托管堆使用的是分代标记清除算法。
标记清除算法
首先,系统将托管堆内所有的对象视为可以回收的垃圾,然后系统从GCRoot开始遍历托管堆内所有的对象,将遍历到的对象标记为可达对象,在遍历完成之后,回收所有的非可达对象,完成一遍垃圾收集。
注意,托管堆的垃圾收集只会自己收集托管对象!
由于在执行完垃圾收集之后,托管堆中会产生很多的内存碎片,导致内存不再连续,因此在垃圾收集完成之后,系统会执行一次内存压缩,将不连续的内存重新排列整齐,变成连续的内存。(关于垃圾收集的详细信息,大家可以参考《CLR Via C#》)
通过上面的简述,大家都知道什么样的对象不会被收集,即能从GCRoot开始遍历到的对象。
最常见的GCRoot是线程的栈,线程的栈里面通常包含方法的参数、临时变量等。另外常见的GCRoot还有静态字段、CPU寄存器以及LOH堆上的大的集合。因此,如果想要让托管对象的内存顺利的释放,只需要断开与跟之间的联系即可。而对于非托管对象的内存,必须进行手动释放。
下面我根据自己在优化内存的过场中的一些常见错误以及一些解决方法。
事件
在.net内存泄露的原因当中,事件占据了非常大的一部分比例,事件是一种委托的实例,也就是与我们类中其他的字段一样,也是一个字段。
委托为什么能阻止垃圾收集呢?即委托是如何让相关的对象在垃圾收集的时候被标记为可达对象的呢?首先要从委托的本质看起,
我们通常使用的委托是从类
public abstract class MulticastDelegate : Delegate |
继承的,MulticastDelegate内部维护了一个private object _invocationList;,即我们通常所有的委托链(ps:委托链同字符串一样,是不可变的),这个委托链是以个object [],内部保存了Delegate对象,及每一个委托实际上是一个Delegate对象,而Delegate包含了两个非常重要的字段:
|
其中_target就是订阅事件的对象,_methodBase则是订阅事件的方法的 MethodInfo。其关联关系如下例所示:
|
我们假设此段代码所在的对象即为一个可达的对象,则其引用关系如下图所示:
由上图我们可以看出,原本应该在方法结束后就可以变为不可达对象的tempTestEvent变成了可达对象,因此也不能对其进行收集了。
个人建议:将类中所有的事件订阅添加到一个专门的方法当中,且实现一个与其匹配的取消订阅的方法,并在必要的时候,调用取消订阅的方法。
非托管对象
非托管对象无论在什么时候,都不会被垃圾收集所回收,必须手动释放。
.net中的非托管资源都实现了IDispose接口,我们可以在使用的时候,使用using(){}类实现非托管资源的释放。
其中有一种情况非常容易遗漏,即通过一个方法创建了一个非托管的对象,如下所示:
|
大家在使用的时候非常容易遗忘通过这种方法的形式创建的非托管对象,尤其是一些名字意义表达不准确的时候,例如
var temp = CreateATemp();//CreateATemp返回一个非托管对象 |
大家可能会漏掉对temp的内存释放,因此建议大家尽量少用方法创建或者初始化非托管对象,如果需要,则使用如下的方式:
|
即使用out关键字,这样大家在使用这个方法的时候,需要首先声明相关的非托管对象,可以在使用完成之后,及时的释放,减少遗漏。
集合/静态字段
对于集合,我们在使用完成之后,需要即时的clear,尤其是将一些方法中的临时变量添加到集合当中之后,会导致集合膨胀,并使得其中的内存泄露。
对于静态字段,我们应该尽量减少其可见的域,因为静态字段在整个程序运行期间都不会被释放,减少其可见域就减少了其内存泄露的可能性,注意,不到万不得以,千万不要声明静态的集合,就是使用了,那也一定要小心再小心。静态集合很容易造成内存泄露。
最好,大家有什么好的建议后者方法,欢迎补充!!
分享.net常见的内存泄露及解决方法的更多相关文章
- JS高程中的垃圾回收机制与常见内存泄露的解决方法
起因是因为想了解闭包的内存泄露机制,然后想起<js高级程序设计>中有关于垃圾回收机制的解析,之前没有很懂,过一年回头再看就懂了,写篇博客与大家分享一下. #内存的生命周期: 分配你所需要的 ...
- Android APP常见的5类内存泄露及解决方法
1.static变量引起的内存泄漏 因为static变量的生命周期是在类加载时开始 类卸载时结束,也就是说static变量是在程序进程死亡时才释放,如果在static变量中 引用了Activity 那 ...
- 调用MediaScannerConnection 发生内存泄露的解决方法
调用MediaScannerConnection发起扫描时经常会发生内存泄露,例如: E ActivityThread: Activity FolderListActivity has leaked ...
- java中常见的内存泄露的例子
JAVA 中的内存泄露 Java中的内存泄露,广义并通俗的说,就是:不再会被使用的对象的内存不能被回收,就是内存泄露. Java中的内存泄露与C++中的表现有所不同. 在C++ ...
- JavaScript内存泄露,闭包内存泄露如何解决
本文原链接:https://cloud.tencent.com/developer/article/1340979 JavaScript 内存泄露的4种方式及如何避免 简介 什么是内存泄露? Java ...
- 谈谈.NET中常见的内存泄露问题——GC、委托事件和弱引用
其实吧,内存泄露一直是个令人头疼的问题,在带有GC的语言中这个情况得到了很大的好转,但是仍然可能会有问题.一.什么是内存泄露(memory leak)?内存泄露不是指内存坏了,也不是指内存没插稳漏出来 ...
- JavaScript 中 4 种常见的内存泄露陷阱
了解 JavaScript 的内存泄露和解决方式! 在这篇文章中我们将要探索客户端 JavaScript 代码中常见的一些内存泄漏的情况,并且学习如何使用 Chrome 的开发工具来发现他们.读一读吧 ...
- .NET中常见的内存泄露问题——GC、委托事件和弱引用
一.什么是内存泄露(memory leak)? 内存泄露不是指内存坏了,也不是指内存没插稳漏出来了,简单来说,内存泄露就是在你期待的时间内你程序所占用的内存没有按照你想象中的那样被释放. 因此什么是你 ...
- JavaScript 中常见的内存泄露陷阱(摘)
内存泄露是每个开发者最终都不得不面对的问题.即便使用自动内存管理的语言,你还是会碰到一些内存泄漏的情况.内存泄露会导致一系列问题,比如:运行缓慢,崩溃,高延迟,甚至一些与其他应用相关的问题. 什么是内 ...
随机推荐
- IOS程序创建view
在IOS程序中创建view有六种方式 首先创建一个GLViewController类,继承UIViewController. 然后进入GLAppDelegate.m,在- (BOOL)applicat ...
- github basic usage in windows
1. create a new accout, create orginazation, create repo 2. install git in your local pc Note: you c ...
- (64位oracle使用32位的PLSQL)安装64位的oracle数据库软件,使用32位的PLSQL Developer连接方法
因为PLSQL Developer没有提供64位的,于是依据网上的资料做了一下整理,发上来 1.下载并安装Oracle 11g R2 64位,在server上安装时忽略硬件检測失败信息: 2.下载Or ...
- Android高效的应用程序开发工具集1---ant构建一个简单的Android工程
在java编译那些事通过提到ant编译Java工程,如今扩大到用它来构建Android目,事实上道理是相通的.变化的仅仅是使用的形式.ant构建相比IDE的优点是多个子项目使用自己定义jar包时,an ...
- android数据库升级的措辞
在基类table增加upgrade操作: public abstract class DbBaseTable { private static final String TAG = "DbB ...
- leetcode 第43题 Wildcard Matching
题目:(这题好难.题目意思类似于第十题,只是这里的*就是可以匹配任意长度串,也就是第十题的‘.*’)'?' Matches any single character. '*' Matches any ...
- Android学习路径——Android的四个组成部分activity(一)
一.什么是Activity? Activity简单的说就是一个接口.我们是Android手机上看到的每个界面就是一个activity. 二.Activity的创建 1.定义一个类继承activity, ...
- redmine的邮件配置
redmine的邮件配置 2012-01-04 18:09:21| 分类: 默认分类|举报|字号 订阅 redmine里要用到邮件通知,本来以为很是简单,网上也有许多教程,谁知忙活了一下午, ...
- ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork 制作一个添加新闻功能
本文将交大伙怎么集成ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork来制作一个新闻系统 先上截图: 添加页面如下: 下面来看代码部分 列表页如下: @ ...
- HttpRuntime详解分析
HttpRuntime详解分析(上) 文章内容 从上章文章都知道,asp.net是运行在HttpRuntime里的,但是从CLR如何进入HttpRuntime的,可能大家都不太清晰.本章节就是通过深入 ...