.NET 垃圾回收与内存泄漏
原文:.NET 垃圾回收与内存泄漏

> 前言
相信大家一定听过,看过甚至遇到过内存泄漏。在 .NET 平台也一定知道有垃圾回收器,它可以让开发人员不必担心内存的释放问题,因为它会自定管理内存。但是在 .NET 平台下进行编程,绝对不会发生内存泄漏的问题吗?答案是否定的,就算有了自动内存管理的垃圾回收器,也会发生内存泄漏。本文就讨论下 .NET 平台的垃圾回收器是如何工作的,进而当我们在编写 .NET 程序时避免发生内存泄漏的问题。

> 垃圾回收的基本概念
“垃圾”指的是事先分配过但后来不再被使用的内存。
垃圾回收背后的一个基本观念是:“无限访问的内存”,但是从来没有无限的内存,当机器需要分配内存但不够的时候,就需要把之前不再使用的内存——“垃圾”回收再利用。
.NET 的垃圾回收器正是这样做的:
.NET Framework 的垃圾回收器管理应用程序的内存分配和释放。每当您创建新对象时,公共语言运行时都会从托管堆为该对象分配内存。只要托管堆中有地址空间可用,运行时就会继续为新对象分配空间。 但是,内存不是无限大的。最终,垃圾回收器必须执行回收以释放一些内存。(引用 MSDN 垃圾回收)

> 垃圾回收器的工作场景
每当我们创建一个对象的时候,系统会为新对象分配一块内存,如果有足够的可用内存则会直接分配;但是当内存不足的时候,此时垃圾回收器会进行一次回收操作,把不再使用的对象释放,转化为可用的内存供新对象使用。

看似很简单的工作步骤,但是垃圾回收器怎么知道确保不再使用的对象的呢?

> 垃圾回收算法
当进行一次垃圾回收操作时,会分三个步骤进行:
1. 先假设所有对象都是垃圾;
2. 标记出正在使用的对象;
  标记依据:
  a. 被变量引用的对象,仍然在作用域中。
    比如某个类中的某个方法,方法执行了一半,如果此时发生垃圾回收,那么方法块中的变量都在作用域中,那么它们都会被标记为正在使用。
  b. 被另一个对象引用的对象,仍在使用中。
3. 压缩:释放第二步中未标记的对象(不再使用,即“垃圾”)并将使用中的对象转移到连续的内存块中。
  只要垃圾回收器释放了能释放的对象,它就会压缩剩余的对象,把它们都移回堆的端部,再一次形成一个连续的块。

备注:
垃圾回收器为了提升性能,使用了代机制,新建的对象是新一代,较早创建的对象是老一代,最近创建的对象是第0代。为了描述垃圾回收器的基本原理,本文不深入讨论代机制。

总之,有了垃圾回收器,我们不必自己实现代码来管理应用程序所用的对象的生存期。

既然有了自动内存管理功能的垃圾回收器,为什么还会发生内存泄漏呢?

> 托管与非托管
由公共语言运行库环境(而不是直接由操作系统)执行的代码称作托管代码,运行在 .NET 框架下,受 .NET 框架管理的应用或组件称作托管资源。.NET 中超过80%的资源都是托管资源,如 int, string, float, DateTime。
非托管资源是 .NET 框架之外的,最常见的一类非托管资源就是包装操作系统资源的对象,例如文件,窗口或网络连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它不了解具体如何清理这些资源。所以,对于非托管资源,在应用程序中使用完之后,必须显示的释放它们。
所以,大部分内存泄漏都是非托管资源内存泄漏:没有显示的释放它们。

> 非托管资源内存泄漏
一个会导致内存泄漏的类:

public class Foo
{Timer _timer;public Foo(){_timer = new Timer(1000);_timer.Elapsed += _timer_Elapsed;_timer.Start();}void _timer_Elapsed(object sender, ElapsedEventArgs e){Console.WriteLine("Tick");}
}

调用 Foo 类:

static void Main(string[] args)
{Foo foo = new Foo();foo = null;Thread.Sleep(int.MaxValue);
}

foo 虽然设置为 null,但是 foo 中的字段 _timer 依然存活,Elapsed 事件继续执行:

此类中,_timer 对象就是非托管对象,由于 _timer 的 Elapsed 事件,.NET Framework 会保持 _timer 永远存活,进而 _timer 对象会保持 Foo 实例永远存活,直到程序关闭。

为了解决这个问题,我们要显示的释放 _timer 对象:Foo 类继承 IDisposable 接口,修改后的类:

public class Foo : IDisposable
{Timer _timer;public Foo(){_timer = new Timer(1000);_timer.Elapsed += _timer_Elapsed;_timer.Start();}public void Dispose(){Console.WriteLine("Dispose");_timer.Dispose();}void _timer_Elapsed(object sender, ElapsedEventArgs e){Console.WriteLine("Tick");}
}

再次调用 Foo 类,并显示调用 Dispose 方法:

static void Main(string[] args)
{Foo foo = new Foo();foo.Dispose();foo = null;Thread.Sleep(int.MaxValue);
}

foo 设置为 null,_timer 对象也同时被回收,Elapsed 事件停止:

> 非托管资源的垃圾回收
1. 析构函数。
2. 实现 IDisposable 接口。
  在我们编写代码时,一个简单的方法就是查看类中定义的字段是否有继承 IDisposable 接口的,如果有,那么当前的类也应继承 IDisposable 接口。在使用完非托管资源时,要及时调用 Dispose 方法释放资源:

Label label = new Label();
this.Controls.Add(label);
this.Controls.Remove(label);
label.Dispose();

更好的方式是使用 using,using 会在编译代码的时候自动创建 try/finally 语句块,在 finally 语句块中自动调用 Dispose 方法。

using (Label label = new Label())
{this.Controls.Add(label);this.Controls.Remove(label);
}

> 避免内存泄漏的几点建议
除了刚刚提到的非托管资源,还有几点需要注意:
1. 订阅事件,不再使用时要记得取消订阅。
2. 不要大量使用静态字段,静态字段会永远存活,一个静态的集合很容易引起内存溢出。

posted on 2014-02-23 00:37 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/3561547.html

.NET 垃圾回收与内存泄漏相关推荐

  1. js内存泄露 垃圾回收_Java内存体系结构(模型),垃圾回收和内存泄漏

    js内存泄露 垃圾回收 Java内存架构(Java内存模型) 上面是堆的Java内存模型以及Java虚拟机(JVM)中运行的任何Java应用程序的PermGen. 还提供了比率,以使您更好地了解如何在 ...

  2. 【Unity】Unity内存管理与优化(一)内存域、堆栈、垃圾回收、内存泄漏、内存碎片

    文章目录 Unity内存 内存域 - 托管域 - 本地域 - 外部库 - 跨桥操作 堆和栈 - 栈 - 堆 - 堆栈的使用 垃圾回收 - Mono内存分配过程 - 内存泄漏 - 内存碎片 - 运行时垃 ...

  3. Java内存体系结构(模型),垃圾回收和内存泄漏

    Java内存架构(Java内存模型) 上面是堆的Java内存模型以及Java虚拟机(JVM)中运行的任何Java应用程序的PermGen. 还提供了比率,以使您更好地了解如何在每种世代类型之间分配允许 ...

  4. JavaScript中的垃圾回收和内存泄漏

    前言 程序的运行需要内存.只要程序提出要求,操作系统或者运行时就必须供给内存.所谓的内存泄漏简单来说是不再用到的内存,没有及时释放.为了更好避免内存泄漏,我们先介绍Javascript垃圾回收机制. ...

  5. 浏览器中的垃圾回收与内存泄漏

    1. 介绍 浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存.其原理是:垃圾收集器会定期(周期 ...

  6. 浏览器的垃圾回收及内存泄漏的情况

    通过 4 个问题,来了解浏览器垃圾回收的过程,后面会逐一解答: 浏览器怎么进行垃圾回收? 浏览器中不同类型变量的内存都是何时释放? 哪些情况会导致内存泄露?如何避免? weakMap weakSet ...

  7. .NET深入学习笔记(3):垃圾回收与内存管理

    今天抽空来讨论一下.Net的垃圾回收与内存管理机制,也算是完成上个<WCF分布式开发必备知识>系列后的一次休息吧.以前被别人面试的时候问过我GC工作原理的问题,我现在面试新人的时候偶尔也会 ...

  8. 深入了解C#系列:谈谈C#中垃圾回收与内存管理机制

    今天抽空来讨论一下.Net的垃圾回收与内存管理机制,也算是完成上个<WCF分布式开发必备知识>系列后的一次休息吧.以前被别人面试的时候问过我GC工作原理的问题,我现在面试新人的时候偶尔也会 ...

  9. Java虚拟机(二)——垃圾回收与内存分配

    文章目录 垃圾回收与内存分配 1 引用 2 如何判断对象需要回收 2.1 引用计数算法 2.2 可达性分析算法 2.3 废弃常量的回收 2.4 无用的类的回收 3 内存分配 4 垃圾收集算法 4.1 ...

最新文章

  1. 对装饰器@wraps的解释(一看就懂)-- 并对装饰器详解
  2. Loadrunner11点击录制脚本无响应,IE页面弹不出——解决方案汇总
  3. python的property用法_python @property的用法及含义全面解析
  4. 【人工智能实战2019-何峥】第1次作业
  5. 得到一个汉字的拼音第一个字母
  6. mysql心得笔记_mysql总结笔记
  7. VIM空格和TAB转换
  8. pdf417条形码开发
  9. 开源协议比较:BSD、Apache、GLP、LGLP、MIT
  10. 【转载】云风skynet服务端框架研究
  11. 信息搜集-敏感信息泄露
  12. SpringMVC的基本使用+原理,一篇囊括
  13. SQL Server2008从入门到精通pdf
  14. 菜鸟之如何让项目跑起来(适合小白看,不是小白的不要进来看了,浪费时间)
  15. 教你亲手制作一个虚拟数字人,超全步骤详解
  16. 20194616 第一次作业
  17. EffectiveC++详解:条款05-了解C++默默编写并调用哪些函数
  18. kubernetes开发环境的比较
  19. 前端逐帧动画性能探究和比较
  20. Tradeoff 是一种针对目标选择有效的路径的思维方式

热门文章

  1. Sublime text的必要配置
  2. 转--linux开启FrameBuffer
  3. WCF我应该再深入一些
  4. python进程数上限_在多处理python中限制进程数
  5. 一步一步手绘Spring IOC运行时序图二(基于XML的IOC容器初始化)
  6. [Java] 蓝桥杯ALGO-11 算法训练 瓷砖铺放
  7. 蓝桥杯 ALGO-143 算法训练 字符串变换
  8. 攀枝花a货翡翠,晋城a货翡翠
  9. WEB应用支持RESTFUL风格方法
  10. Spark 整合ElasticSearch