《Effective C#》读书笔记——了解.NET内存管理机制<.NET资源管理>

简介:

 我们知道C#是一门虚拟机语言,在C#编译器首先将C#代码编译成IL代码,运行程序时CLR(Common Language Runtime,公共语言运行时)通过调用JIT(just-in-time Compiler即时编译器)来将IL代动态即时编译成可执行的机器码。在CLR中有一个非常重要的概念:CLR GC(Garbage Collector,垃圾收集器),GC自动为我们的应用程序进行内存管理的分配和释放,所以在.NET应用程序中一般很少出现内存泄露、悬挂指针、未初始化指针等问题。同时这也是C#这样的虚拟机语言和C/C++等本地语言最大的不同之处。

  但是GC也不是万能的,我们也需要自己做一些清理工作,管理那些非托管的对象,它们通常是包装操作系统资源的对象,例如,文件句柄、窗口句柄或网络连接

这是因为外部的资源已经超出了运行时(虚拟机)的能力范围,而这些非托管的资源是由操作系统来掌控的。

同时如果使用了事件处理函数和委托在对象之间建立了链接,也可能会造成对象超过你预期的长时间驻留在内存中同时类似于LINQ的这样的在只有请求结果是才真正执行的查询也会导致同样的问题(因为查询将把需要绑定的局部变量使用闭包封装起来,而绑定对象只有在查询结果离开作用域时才会被释放掉)。可以看出在托管环境中开发应用程序,了解CLR内存的管理机制,对于我们写出更富有效率和健壮的代码是非常有益的。

阅读目录

  1.标记并压缩算法(Mark and Compact)

  2.清理非托管资源

      2.1 实现终结器

      2.2 实现IDisposable接口

  3.小节

  进一步阅读和参考

 

1.标记并压缩算法(Mark and Compact)

   GC使用"标记并压缩"(Mark and Compact)算法分析程序运行对象的关系,将不可达部分对象从内存中清理掉。GC会从应用程序的根对象开始,通过对对象树形结构遍历来判定一个对象是否可达,而并不是向COM那样跟踪每个对象的引用。

  我们可以假设有一个EntitySet,是从数据库中读取得到的一系列对象的集合。每个Entity对象都可能包含对其他Entity对象的引用,还可能包含对其他Entity的连接。类似于关系型数据库中的实体集合一样,这些链接和引用也可能是循环的。每个EntitySet中都包含了一系列对象图之间的复杂引用。但是这些并不需要我们担心,GC会替我们很好的完成这个任务。GC通过判断某一个对象(图)是否为垃圾来简化这一操作——当应用程序不需要某一个对象时,将不会保留这个对象的引用。GC会将那些没有被任何对象之间或间接引用的对象判定为垃圾。

  GC在其专门的线程中运行,GC不但自动为程序清理不在使用的内存,还会在每次运行时压缩托管堆。因此内存中空余的空间会是一片连续的内存。

下面图为一个托管堆在一次垃圾收集前后的状态:

我们可以看到GC不但清理不在使用的内存,还会移动内存中的其他对象,以便压缩正在使用的内存,并提高可用的内存空间。

 

2.清理非托管资源

  现在我们知道:托管堆上的内存管理是GC的职责。但是,非托管资源的内存管理则需要由我们开发者自己来负责。.NET为我们提供了两种控制非托管资源生命周期的机制:终结器(finalizer)和IDisposable接口。

 

2.1 (终结器)finalizer

  通过调用Finalize()方法通知GC回收该对象使用的内存时同时清理其非托管资源。终结器是一种防御性手段,让你的对象不管如何都可以被释放掉其中使用的非托管资源。终结器由GC调用,调用将发生在对象成为垃圾之后的某个时间(我们并不能准确的知道其发生的具体时间,只是知道这将发生在对象不可达之后)。这和C++相比区别很大,有经验的C++开发者往往会在构造函数中分配关键资源,然后在析构函数中释放掉。

2.1.1 使用终结器的问题

  在C#中,终结器迟早都会执行,不过其执行时间却无法预料(也不能)。终结器仅仅能够保证给定类型的对象所分配的非托管资源最终会被释放,但其执行时间确实不可预料的,因此:在我们的代码中最好避免使用终结器,也应该尽量少让代码逻辑使用到终结器

  依赖终结器还对带来性能上的损失。需要执行终结器的对象会给GC带来额外的性能开销。当GC发现某个对象属于垃圾,而该对象又需要执行终结器时,就不能将其直接从内存中移除:首先,GC将调用终结器,而终结器并不在执行垃圾收集的线程上执行。GC将把所有需要执行终结器的对象放在专门的队列中,然后让另一个线程来执行这些对象的终结器。用 Finalize 方法回收对象使用的内存需要至少两次垃圾回收。所有我们知道使用终结器释放非托管资源,资源对象不但会在内存中停留更长时间,GC也需要额外的线程来运行

2.1.2 GC中"代"(Generation)的概念

  .NET的GC为了优化其执行,引入了“代”(Generation)的概念。这概念可以让GC更快速的找到那些更有可能是垃圾的对象。

自上一次垃圾收集以后,新创建的对象属于第0代对象。而如果经过上一次垃圾收集之后仍旧存活的对象,将成为第1代对象。两次以及两次以上的垃圾收集仍没有被销毁的对象就变成了第2代对象。

  这样的分代方式是为了能将局部变量和应用程序生命周期中一直使用的对象分开对待。第0代大多属于局部变量。而成员变量和全局变量则会变成第1代对象,直至变成第2代对象。而GC通过减少检查第1代和第2代的次数来优化执行过程。大概10个周期的GC中,只有一次会同时检查第0代和第1代对象;大概100个周期的GC中,会有一次同时检查所有对象。所有,一个需要终结器的对象可能会比普通的对象多停留9个GC周期。而对于第2代对象,甚至需要100次以上的GC周期才有机会被销毁。

 

2.2 IDisposable接口

   相较于终结器IDisposable接口能够以一种影响更小的方式及时的释放非托管资源。使用IDisposable接口释放非托管资源可以避免执行终结器给GC带来性能上的影响。实现IDisposable接口的 Dispose 方法应该释放它拥有的所有资源。 它还应该释放其基类型拥有的所有资源通过调用其父类型的 Dispose 方法。

 

小节:

  GC自动为我们的应用程序进行内存管理的分配和释放,所以在.NET应用程序中一般很少出现内存泄露、悬挂指针、未初始化指针等问题。非托管的资源要使用终结器来保证释放。即使终结器会对程序的性能带来很大的影响,我们也必须提供终结器这是,这是为了保证不出现资源泄露的问题。但是,使用使用IDisposable接口释放非托管资源可以避免执行终结器给GC带来性能上的影响。

 

参考&进一步阅读

MSDN:自动管理内存

MSDN:托管执行过程

MSDN:清理非托管资源

MSDN:深入探讨IDisposable

蔡学镛:.NET的自动内存管理

维基百科:公共语言运行时

《Effective C#》

本文转自gyzhao博客园博客,原文链接:http://www.cnblogs.com/IPrograming/archive/2012/10/22/Effective_CSharp_Unit2.html,如需转载请自行联系原作者
相关文章
|
8天前
|
监控 算法 Java
Java中的内存管理:理解Garbage Collection机制
本文将深入探讨Java编程语言中的内存管理,特别是垃圾回收(Garbage Collection, GC)机制。我们将从基础概念开始,逐步解析垃圾回收的工作原理、不同类型的垃圾回收器以及它们在实际项目中的应用。通过实际案例,读者将能更好地理解Java应用的性能调优技巧及最佳实践。
33 0
|
12天前
|
消息中间件
共享内存和信号量的配合机制
【9月更文挑战第16天】本文介绍了进程间通过共享内存通信的机制及其同步保护方法。共享内存可让多个进程像访问本地内存一样进行数据交换,但需解决并发读写问题,通常借助信号量实现同步。文章详细描述了共享内存的创建、映射、解除映射等操作,并展示了如何利用信号量保护共享数据,确保其正确访问。此外,还提供了具体代码示例与步骤说明。
|
24天前
|
SQL 存储 关系型数据库
C#一分钟浅谈:使用 ADO.NET 进行数据库访问
【9月更文挑战第3天】在.NET开发中,与数据库交互至关重要。ADO.NET是Microsoft提供的用于访问关系型数据库的类库,包含连接数据库、执行SQL命令等功能。本文从基础入手,介绍如何使用ADO.NET进行数据库访问,并提供示例代码,同时讨论常见问题及其解决方案,如连接字符串错误、SQL注入风险和资源泄露等,帮助开发者更好地利用ADO.NET提升应用的安全性和稳定性。
58 6
|
20天前
|
算法 Java 中间件
C#/.NET/.NET Core优质学习资料,干货收藏!
C#/.NET/.NET Core优质学习资料,干货收藏!
|
20天前
|
人工智能 开发框架 算法
C#/.NET/.NET Core技术前沿周刊 | 第 2 期(2024年8.19-8.25)
C#/.NET/.NET Core技术前沿周刊 | 第 2 期(2024年8.19-8.25)
|
20天前
|
缓存 开发框架 算法
C#/.NET这些实用的编程技巧你都会了吗?
C#/.NET这些实用的编程技巧你都会了吗?
|
20天前
|
传感器 应用服务中间件 Linux
C#/.NET/.NET Core技术前沿周刊 | 第 3 期(2024年8.26-8.31)
C#/.NET/.NET Core技术前沿周刊 | 第 3 期(2024年8.26-8.31)
|
20天前
|
人工智能 算法 C#
C#/.NET/.NET Core技术前沿周刊 | 第 1 期(2024年8.12-8.18)
C#/.NET/.NET Core技术前沿周刊 | 第 1 期(2024年8.12-8.18)
|
1月前
|
JSON C# 开发者
💡探索C#语言进化论:揭秘.NET开发效率飙升的秘密武器💼
【8月更文挑战第28天】C#语言凭借其强大的功能与易用性深受开发者喜爱。伴随.NET平台演进,C#持续引入新特性,如C# 7.0的模式匹配,让处理复杂数据结构更直观简洁;C# 8.0的异步流则使异步编程更灵活高效,无需一次性加载全部数据至内存。通过示例展示了模式匹配简化JSON解析及异步流实现文件逐行读取的应用。此外,C# 8.0还提供了默认接口成员和可空引用类型等特性,进一步提高.NET开发效率与代码可维护性。随着C#的发展,未来的.NET开发将更加高效便捷。
44 1
|
1月前
|
JavaScript 前端开发 算法
js 内存回收机制
【8月更文挑战第23天】js 内存回收机制
32 3