JVM和CLR的垃圾回收对比

 # 分代&回收算法

  垃圾收集算法一般有标记-清除、复制算法、标记-整理、分代收集

JVM CLR
  • 年轻代 8(Eden) : 1(Survivor from) : 1(Survivor to) 
  • 老年代

 分代收集,新生代->复制算法。老年代->标记-整理

  • 第0代
  • 第1代
  • 第2代

会根据每次回收的大小动态调整每代的内存容量,回收算法->标记-清除-压缩

 

# 触发条件

   不讨论例如代码手动调用GC、系统内存不足、进程退出等情况

JVM CLR
新生代(minor gc) 老年代(full gc)

 

  1. 第0代内存不够分配当前所需内存时回收第0代
  2. 第1代内存不够分配当前所需内存时回收第1代
  3. 第2代内存不够分配当前所需内存时回收第2代
  1. eden无法分配足够内存时开始minor gc
  1. 老年代无法分配所需内存时(新生代代提升对象到老年代)
  2. 每次minor gc时判断之前每次存入老年代对象的平均内存是否大于当前 剩余内存,如果大于当前剩余内存,再判断HandlePromotionFailure是否允许担保失败,如果允许则放弃full gc继续minor gc,如果不允许(默认不允许) 则开始一次full gc


# 如何判断对象是否可以被回收
 

 常用算法有引用计数、根搜索

JVM CLR
  1.  通过名为"GC Roots"的根作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链则认为这个对象不可达,可以被回收。在回收之前,如有需要(是否重写finalize)还会执行对象的finalize方法,如果在此方法中重新将对象赋值给外部变量,则放弃对该对象的回收,否则回收该对象。单一对象只会执行一次finalize方法,下次回收时不会再次执行,所以对象有且只有一次自救机会。
  1.  首先标记所有对象为可回收(同步块索引字段的一位设置为0),然后CLR检查所有活动根,查看它们引用了哪些对象,被引用的对象修改同步块索引的位设为1,标记不可被回收

 java中可作为GC Roots的对象包括下面几种:

  • 虚拟机栈(栈帧中的本地变量表)中的引用的对象
  • 方法区中的类静态属性引用的对象
  • 方法区中的常量引用的对象
  • 本地方法栈中JNI(即一般说的Native方法)的引用的对象
 C#中可作为根的对象包括类的静态和实例字段、方法的参数和局部变量

 

# 如何回收

JVM CLR
新生代(复制算法) 老年代(标记-整理)
  1. 标记所有不可达对象,回收所有不可达对象,压缩内存(标记-清除-压缩)
  1. 新建对象分配到eden,如果eden剩余内存不够时,启动minor gc,回收eden和survivor from中不可达对象,并将eden、survivor from中存活的对象移动到survivor to中,互换survivor from 和survivor to
  1. 回收不可达对象,移动存活对象到老年代的一端,保证内存的连续性

 

# 提升代的条件

 JVM  CLR
  1.  回收次数大于MaxTenuringThreshold(默认15)值还存活的对象(长期存活)
  2. 每次回收存活对象总和大于survivor空间的所有对象(分配担保)
  3. 内存超过PretenureSizeThreshold值的对象(大对象)
  4. Survivor中同龄对象大小总和大于Survivor的一半,当前年龄及以上的对象(动态年龄判断)
  1. 每次存活下来的对象都会提升一代 
posted @ 2018-08-09 14:42  小孑  阅读(725)  评论(0编辑  收藏  举报