垃圾回收--代
垃圾回收--代
代是CLR垃圾回收器采用的一种机制,他唯一的目的就是提升应用程序的性能,采用代的垃圾回收器做到了一下几点:
1:对象越新,生存周期越短,跟栈的原理很像,先进后出,先定义的局部变量,在栈中停留的时间相对长一点。
2:对象越老,生存周期越长,后面解释。
3:回收堆的一部分,速度快于回收整个堆,那是肯定的,就是为了实现只回收一部分内存中的数据,才产生了代的概念,大多数时间只回收第0代。
第0代:托管堆在初始化的时候不包含任何对象,新分配在堆上的对象被称为第0代,垃圾回收器从没有检查过他,CLR在初始化的
时候,会为第0代预算了一个容量,假设为256KB,容量一般为128KB的整数倍,这跟CPU的L2缓存容量有关,CPU的L2缓存容量经
历了128KB,256KB,512KB,1M......,就是为了让第0代中的数据能全部装入CPU的L2缓存中,这样处理数据会更快。
第1代:如果在分配一个新对象时,使第0代中的数据超过其预算容量,就会发生垃圾回收,不能到达的数据就会被回收,不能到
达就是没有被任何对象引用,有被引用当然就不会被回收,没有被回收的数据,被称为第1代,第一代的容量比第0代的大,假设
为2M,经过一次垃圾回收后第0代不包含任何数据。只要第1代没有就不会回收。
新产生的对象永远被分配在第0代中,第0代中的数据满了,就发生垃圾回收,将没有被回收的对象压缩至第1代,数据压缩相当耗
性能,就是把数据从内存在的A出复制到内存的B处,只要第1代中的数据没有满,垃圾回收器就只会回收第0代中的数据,这就是
部分回收,当第1代中的数据满了,垃圾回收器就会同时回收第0代和第1代,第0代没有被回收的数据就会被压缩到第1代中,直至
第1代也满。
第2代:当第1代中的数据满了,就会回收第1代,没有被回收的数据就被称为第2代,第2代的容量大于第1代,托管堆只支持3代
,第0代,第1代,第2代。
CLR假设新生成的对象生存周期较短,所以每次回收第0代都能回收大量内存,所以CLR总是针对第0代疯狂的回收,这样效率高吗
?哈哈哈!这样可能导致第0代被回收了很多次,第1代一次也没有被回收,当然第1代中大垃圾也就没有被回收,CLR假设活得比
较久的对象能继续活下去,这就是对象越老,生存周期越长。
托管堆只实现部分回收,即回收第0代,由于第0代的容量较小,所以每次垃圾回收的速度较快,当然也会因为第0代容量较小,发
生垃圾回收的频率会高一些,为了得到更好的垃圾回收性能,所以第0代的容量是动态的,如果每次执行垃圾回收,所有的垃圾都
被回收了,那么托管预算第0代的容量就会减少,这样可以加快垃圾回收的速度,如果每次执行垃圾回收,都有大量的垃圾没有被
回收,从第0代变成第1代,托管堆就会将第0代的预算容量变大,这样可以减少数据的转移。数据的转移就相当于整理内存碎片,
对程序的性能,影响还是蛮大的。
只回收第0代的还有一个好处就是,如果第0代中的数据引用了第1代中的数据,第1代中的数据不用被检查,第1代中被引用的对象
内部结构,垃圾回收器也不会管,回收更快啊,当然第1代中的数据也可能引用第0代中的数据,若第1代中的对象引用了第0代中
的对象,第1代中的对象就会被标记,只有重上一次垃圾回收到下一次垃圾回收被标记的对象才会被检查,当然要检查第1代中的
数据啊,你不检查第1代中的数据,你怎么知道第0代中的数据不可到达,没有被引用呢,第1代中,只有被标记的对象才会被检查
,这样同样能提高垃圾回收的性能。总之,微软设计的这个垃圾回收器处处在提高你的程序性能,他是这么说的,你感觉到了吗?
如果回收3代还是没有内存可分配,就抛出一个OutOfMemoryException异常,告诉你没有内存可分配了,以前在开发一个项目中
就偶尔抛出这个异常,当时我们都不知道怎么回事,原因就是我们构造一个大的Json对象,用的是string+string,导致内存被
吃光了,要是当时知道用StringBuilder,性能将会大大的提高,也不至于OutOfMemoryException异常啊,哈哈哈。
作者:陈太汉