1. 概述:CLR通过垃圾回收来管理已分配的创建在托管堆中的实例。(什么时候回收?回收过程)。也可以通过System.GC类类型自己回收,接着分析怎样用System.Object.Finalize()虚方法和Idisposable接口建立即时释放内部非托管资源的类型。NET4新功能:后台垃圾回收和使用System.Lazy<>泛型类实现的延迟实例化。通过本章,你将会牢固地掌握CLR是怎样管理NET对象的。其实就是自定义变量生存周期而已,但是术语专业,吸引人来学习。
2. 类,对象和引用:new分配对象并返回引用
Car refToMyCar=new Car();//refToMyCar是栈中变量,指向对中的Car内存块。结构是值类型。
何时销毁new的内存块?当一个对象从代码库的任何部分都不可访问时,被销毁。类似于智能指针,但是没得AddRef()等方法而已。
托管堆是一片连续的内存空间,新建的对象会被连续非配,在托管堆上,当堆中内存不足分配时,进行垃圾回收。但垃圾回收具体如何发生,取决于NET平台的版本。(这个和操作系统的内存分配?)
3. 应用程序根的作用:一个托管位置,其中保存着对托管堆上一个对象的引用。
什么时候不需要一个对象?在一次垃圾回收过程中,通过建立对象图(对象A依赖与B等)来检查这些对象是否有活动根,删除垃圾对象,并重新整理内存。
准确地说:垃圾回收器使用两个不同的堆,一个用来专门存储非常大的对象,这个堆在回收周期中较少顾及。
4. CLR试图寻找不可访问的对象时,它不会逐个检查托管堆上的每一个对象。
对象的代:对象在堆上存在的时间越长,它就更可能应该保留,
第0代:从没有被标记为回收的新分配的对象;
第1代:在上一次垃圾回收中没有被回收的对象(被标记为已回收,但因为已经获取了足够的堆空间而没有被删除)
第2代:在一次以上的垃圾回收后仍然没有被回收的对象。
注:0,1代被称为暂时代,垃圾回收过程对于暂时代的处理是不同的。
5. NET1.0-NET3.5的并发垃圾回收:在回收0和1代时挂起所有活动线程,在回收2代时,允许程序继续分配堆上的对象。
NET4的后台垃圾回收:后台垃圾回收用于非暂时代,而暂时代专用一个后台线程进行回收。
6. System.GC类型:使用静态成员与垃圾回收器进行交互,在创建那些使用非托管资源的类时需要使用System.GC,例如:使用NET调用基于C的API或者非常低级且复杂的COM互操作逻辑。
7.强制垃圾回收:
a.如果应用程序要进入一段不希望被垃圾回收终端的代码;
b.应用程序刚刚分配了非常多的对象,你想尽可能多地删除已获得的内存。GC.Collect(可以佩戴参数)(会增加对象的代)+GC.WaitForPendingFinalizers();
8. 类型终结器:从Object继承来的Finalize()其实就是析构函数。在使用非托管资源时有可能需要(原始的操作系统文件句柄等资源) 其中:结构由于是值类型,它通过实现Idisponsable接口来实现析构。
Finalize()虽然是虚函数,但是却不能通过override去实现,而是像C++析构函数那样去实现。
9.终结队列:当在托管堆上分配对象时,运行库自动确定该对象是否提供析构Finalize()方法,如果是这样,对象被标记为可终结的,同时一个指向这个对象的指针被保存在名为终结队列的内部队列里。
终结过程:当垃圾回收器要释放一个对象时,先检查终结队列,并将对象从堆上复制到终结可达表的托管结构,下一次垃圾回收将产生另一个线程,为每一个在可达表中的对象调用终结方法。(至少要进行两次垃圾回收)效率很低,本质上是非确定的。
10. 构建可处置对象:实现Idsiposable接口,适合于类和结构,如果类泗洪县了Idisposable,调用Dispose()总是正确的。(例如FileStream虽然调用Close方法,但调用Dispose方法是一样的)
If(rw is IDisposable) rw.Dispose();这样调用不用去查SDK。
using(MyResourceWrapper rw = new MyResourceWrapper(),//一个using只能声明一种类型
rw2 = newMyResourceWrapper()){// Use rw and rw2 objects. }
在using代码段中为每一个声明的对象调用Dispose方法,如果试图使用一个没有实现IDisposable的对象会收到编译器错误。(不一定写了手动调用方法,很麻烦的)
11. 在类里既写析构函数,又写Dispose()方法,并在Dispose中调用GC.SuppressFinalize(this);可以保证正确析构。(代码重复)
12. 但是我们正式的方法是(微软提供的方法):
class MyResourceWrapper : IDisposable //这是一个模板,太死板,但是可以防止多次析构。
{
// Used todetermine if Dispose()
// hasalready been called.
privatebool disposed = false;
public void Dispose()
{
// Callour helper method.
//Specifying "true" signifies that
// theobject user triggered the cleanup.
CleanUp(true);
// Nowsuppress finalization.
GC.SuppressFinalize(this);
}
privatevoid CleanUp(booldisposing)
{
// Besure we have not already been disposed!
if(!this.disposed)
{
// Ifdisposing equals true, dispose all
//managed resources.
if(disposing)
{
//Dispose managed resources.
}
//Clean up unmanaged resources here.
}
disposed = true;
}
~MyResourceWrapper()
{
Console.Beep();
// Callour helper method.
//Specifying "false" signifies that
// the GCtriggered the cleanup.
CleanUp(false);
}
}
弱引用和对象复苏没有分析,自己去分析。
13. 延迟实例化:在代码中添加一个永远不会被使用的特殊成员变量,如果成员变量的初始化需要很大的空间,问起就来了。
可以手工添加一些代码来确保只有在使用时才创建allSongs对象(如使用工厂方法设计模式)
但是NET4提供了Lazy<>泛型类来确保使用时创建
PrivateLazy<AllTracks> allSongs=new Lazy<AllTracks>();
14. 当AllTracks包含其他构造函数,并且你想调用他们,如果建立Lazy<>变量时还有额外的工作要做,又会怎样呢?幸好Lazy<>类允许指定一个泛型委托作为可选参数,类型为System.Func(),它所指向的方法的返回值类型和Lazy<>变量所创建的类型是相同的。
private Lazy<AllTracks> allSongs = new Lazy<AllTracks>( (这里可以指定参数,而不仅仅是默认构造函数) => {
Console.WriteLine("Creating AllTracks object!");
return new AllTracks();}); //注意这里的构造函数和Lamda表达式