生存期垃圾回收器
目前有很多种类型的垃圾回收器.微软实现了一种生存期垃圾回收器(Generation Garbage Collector).
生存期垃圾回收器将内存分为很多托管堆,每一个托管堆对应一个生存期等级。
垃圾回收器目前有三个生存期等级,这里我们称作代,0代,1代,2代,GC中0代是最年轻的对象,2代对象存活的时间最长,GC按代回收垃圾出于性能考虑,通常对象会在0代被回收.
在应用程序初始化之前,所有等级的托管堆都是空的,当对象初始化的时候,他们会按照初始化的先后顺序被放入0代的托管堆中(有例外)。在托管堆中对象是连续存放的,垃圾回收器中保存了一个指针指向托管堆中最后一个对象之后的内存空间
当0代托管堆超过了256K,垃圾回收器检查托管堆中的所有对象,看是否有对象可以回收.
当开始回收时,垃圾回收器找出被继续引用的对象,将这些对象转1代托管堆中,并将0代托管堆的指针移动到开始的位置清除所有的对象,并压缩1代托管堆以保证所有对象之间没有空隙
1代托管堆满了之后,或者1代托管堆没有被垃圾回收器回收,会转移到2代的托管堆
当对象被转移到2代的时候,2代不会被垃圾回收器进行压缩,碎片整理
大对象堆和小对象堆
垃圾回收器根据所占空间大小划分大对象堆和小对象堆,如果一个对象的大小超过85000byte,就认为是一个大对象,当一个对象申请内存大小达到这个阀值,它就会被分配到大对象堆上.
从代的角度看,大对象堆属于2代堆,所以2代堆也叫大对象堆(LOH,large object head),小对象堆(SOH,small object heap)属于0代和1代;前面我们说过2代堆被回收后剩下的对象不会被进行碎片整理,也就是说大堆相对不会被压缩和整理
1,小对象堆释放后,剩下的对象会被碎片整理,压缩合并
2,大对象堆释放后,无效内存留下的空间如果相邻就会被合并,如果不相邻就会单独空在那里;
3,当分配一个大对象时,运行时会优先尝试在LOH的尾部进行分配,如果尾部空间不足,就会尝试向操作系统请求更多的内存空间,只有在这一步也失败时才会重新搜索之前无效对象留下的内存空隙.
下面是大对象堆内存分配问题:
上面的图:
1,LOH中已经存在一个大小为85K的对象和一个大小为16M对象,当需要分配另外一个大小为85K的对象,会在尾部空间;
2,此时发生了一次垃圾回收,大小为16M的对象被回收,其占用的空间为未使用状态,但运行时并没有对LOH进行压缩。
3,此时再分配一个大小为16.1M的对象时,分尝试在LOH尾部分配.但尾部空间不足,所以运行时向操纵系统请求额外的内存,并将对象分配在尾部.
4,此时如果再需要分配一个大小为85K的对象,则优先使用尾部的空间.
OutOfMemoryException内存溢出问题:
当我们每次往后面申请的内存都比前面的大的时候
最后向系统申请内存申请不到,前面也没有任何一块连续区域满足要求时,就会出现内存溢出的问题
大对象什么时候回收:
1,申请的空间超过0代内存大小或者大对象堆的阀值,多数的托管堆垃圾回收在这种情况发生
2,在程序代码中调用GC.Collect方法时;如果在调用GC.Collect方法是传入GC.MAXGeneration参数时,会执行所有代对象的垃圾回收,包括大对象堆的垃圾回收
3,操作系统内存不足时,当应用程序收到操作系统发出的高内存通知时,
4,如果垃圾回收算法认为做二代回收是有收效时会触发二代垃圾回收
解决大对象堆溢出问题
2.尽量“重用”少量的大对象,而不是分配很多大对象;
3.当大对象回收比较慢的时候,可以选择做强制垃圾回收,但频繁回收大对象会损耗系统性能