在创建对象的时候,会把还有析构函数(编译之后,就是Finalize方法,与c++中的析构函数不同)的对象引用存到一个叫做Finalizer Queue的list中,在GC的时候,如果一个对象是无用的,而且在Finalizer Queue里面有引用,此次并不回收,并且会把引用从Finalizer Queue移到Freachable Queue的list中,Freachable Queue的list有内容之后会启动一个线程,然后执行里面的引用的对象的析构函数,执行完毕后把对象的引用删除,等待下次GC的时候,才进行回收此对象。
所以Finalize的特点就是:
啥时候调用不定
这类对象,需要至少两次GC才能回收。
为什么至少两次,而不是两次,因为.net为我们提供了一个把对象引用放回Finalizer Queue的方法,GC.ReRegisterForFinalize(),如果在Finalize中调用了这个代码,那么就死不了了。
微软不建议使用Finalize方法,就像其他博客中提到的,我们可以把它留作后手,万一那个非托管资源该释放没有释放,可以在Finalize方法中做为最后的保险(算是避免人为原因)。
我确实释放完了非托管资源,就是不想执行Finalize方法,微软也提供了方法了:GC.SuppressFinalize(this),这个方法执行了之后就把这个对象的引用中Finalizer Queue移除了。
LOH是什么?LOH(large object heap)是为了大对象而专门设计的一个堆,多大的对象会分配到这个堆里面?超过85000个字节的就会。其实这个loh产生原因大对象移动非常的耗时,还不如不移动,例如,3个对象ABC,AB对象大约占个80个字节,C对象占个10000个字节,假设AB对象被回收,那么在移动阶段,就要把10000个字节,往前移动80字节,还不如不移动性能高。这个85000字节也是一个经验值。
既然loh不能移动,那么肯定不能用Mark and Compact中的移动了(使用什么算法现在还不清楚,猜测是Mark and Sweep,或许是特例),并且在只有2代对象回收的时候才进行回收。
GC模式?workstation mode:用于单处理器的系统中,频繁回收,从而阻止一次长时间的回收对程序的挂起时间。
server mode:用于多处理器的系统中,为每个处理器都创建一个GC Heap,该模式特点是,分配内存较大,能不回收就不回收,回收时候耗时太长。
其中有Concurrent GC 工作方式,其中workstation mode和server mode都可以配置,在单处理器上设置为true也不生效,主要用于用户线程在gc时候可以大部分时间和gc线程并发,详细可以参考:https://blogs.msdn.microsoft.com/seteplia/2017/01/05/understanding-different-gc-modes-with-concurrency-visualizer/
啥时候需要手动gc?资源特别紧张的时候,例如之前面试的一家公司,系统是在azure上,内存什么的特别贵,这个时候手动gc可能其中一种手段了。
最后其实gc的最耗时还是在算法的选择上,比如Mark and Compact中的把内存合并成连续的,这个才是耗时的,如果内存足够多,根本就不需要考虑移动内存。
或者像我之前提的,再在内存上面做一次内存映射的管理,也可以避免内存不连续的问题,当然肯定会遇到各种各样的问题。