19、自动内存管理(垃圾收集)
内存管理:新对象创建、生存期管理、内存资源回收
访问一个资源所需要的步骤:
§ 调用IL中的newobj指令,分配内存
§ 初始化,设置资源初始状态(类型的实例构造器)
§ 通过访问类型成员来使用资源
§ 销毁资源状态,执行清理工作(Finalize, Dispose, Close)
§ 释放引用实例所占的内存(垃圾收集器),而值类型实例的内存随堆栈空间的消亡而自动消失。
垃圾收集算法:
1.垃圾收集只有在第0代对象充满时才会出现。
2.每组应用程序都有一组根,一个根是一个存储位置,其中包含着一个指向引用类型的内存指针。同时,当JIT编译器编译一个方法的IL代码时,除产生本地CPU代码外,JIT编译器还会创建一个内部表,该表的每个条目包含一个方法的本地CPU指令的字节偏移范围,及该范围中的一组包含跟的内存地址。
3.当垃圾收集器开始执行时,它先假设应用程序中没有一个根引用着托管堆中的对象。然后,遍历所有的根,构造出一个包含所有可达对象的图。任何不在该图中的对象都将是应用程序不可访问的对象 ,也是可以被执行垃圾收集的对象。
为什么垃圾收集的功能如此强大,而ANSI C++没有采用它?
这是因为垃圾收集器必须能够识别出应用程序的根,并且还要找到所有的对象指针。非托管C++允许我们进行类型转换,而无从知道它真正引用的对象是什么。在CLR中,托管堆总能知道一个对象的实际类型,从而使其元数据信息来判断一个对象的哪些成员引用着其他的对象。
任何封装了非托管资源的类型,例如文件、网络链接、套接字、互斥体等,都必须支持一种称作终止化(Finalization)的操作。终止化操作允许一种资源在它所占用的内存被回收之前首先执行一些清理工作。
如果一个封装了非托管资源的类型没有定义Finalize方法,那么这些托管资源将得不到关闭,从而导致某种程度的资源泄露,直到进程结束,这些非托管资源才会被操作系统回收。
Finalize方法的问题在于我们不能确定它会在何时被调用,而且它不是公有方法,因此不能显式调用它。
我们可以通过显示调用Dispose方法来释放对象所封装的非托管资源。
调用Dispose或Close方法只是在一个确定的时刻对对象占用的非托管资源执行清理操作而已,但不会控制托管堆中对象所使用的内存的生存期。这意味着我们在调用过Dispose或Close方法之后,对象的内存仍然存在,只是对象被执行资源清理后,不能再成功执行某些方法而已。
C#的using语句:编译时,编译器会自动产生一个try块和一个finally块,在finally块中,编译器会产生代码将变量转型为一个Idisposable接口,并调用其上的Dispose方法。
但我们应谨慎使用C#的using语句,避免过早的执行了资源清理
垃圾收集器中代龄的工作机制:CLR的托管堆支持3个代龄,但初始化时,它会为这三代选择3个阈值容量,选择阈值容量是为了提高系统性能,阈值容量越大,垃圾收集执行的频率也就越低。
另外,CLR垃圾收集器还有一个自调节的能力,会学习应用程序的行为,调节阈值容量,从而改变垃圾收集执行的频率,提高系统性能。
20.CLR寄宿、应用程序域、反射
当一个CLR COM服务器被加载懂啊一个Windows进程中时,它便开始初始化,创建一个托管堆,另外CLR还会创建一个可被加载到当前进程中的所有托管类型使用的线程池。在进行这些初始化工作的同时,CLR还会创建一个应用程序域(AppDomain).
一个应用程序域是一组程序集的一个逻辑容器。应用程序域之间是相互隔离的,可以被卸载的,可以单独实施安全策略和配置策略。
//…………
哈哈,终于很粗略滴把《Applied Microsoft .NET Framework Programming》学习了一遍
收获嘛,有句话叫“看山是山,看水是水”,现在呢?我起码不应该只是照本宣科去堆砌代码,而应多想想为什么这样写,这样写最优么……抓住.NET的核心才能更好的掌握它,希望能一如既往坚持,呵呵。