手绘一张图详细介绍GC回收机制

手绘一张图详细介绍GC回收机制

 

复制算法图解:

 

手绘一张图详细介绍GC回收机制

 

 

上面两张图的高清图地址如下:

https://download.csdn.net/download/u011228868/13455207

https://download.csdn.net/download/u011228868/13455204

GC Root 包括:

  1.  虚拟机栈中引用的对象

  2. 方法区中类静态属性实体引用的对象

  3. 方法区中常量引用的对象

  4. 本地方法栈中JNI引用的对象

清除标记算法伪代码实现如下:

  • 标记实现:
mark_phase(){
   for(r : $roots)//循环遍历空间对象
      mark(*r)
}

mark(obj){
  if(obj.mark == FALSE)
    obj.mark = TRUE
    for(child : children(obj))//对对象树进行遍历循环,递归方式
      mark(*child)
}

通过深度优先遍历对每个节点进行遍历,然后打上标记,对于所有对象哪个是活着的就可以知道了

标记所花费的时间是与存活的数量成正比,时间复杂度为O(N),N为存活的数量

  • 清除算法的简单实现
sweep_phase(){
   sweeping = $heap_start
   while(sweeping < $head_end)
     if(sweeping.mark == TRUE)
        sweeping.mark = FALSE
     else
        sweeping.next = $free_list
        $free_list = sweeping
     sweeping += sweeping.size 
}

在清除阶段程序会遍历整个堆,对有标记的对象把标记清除掉等待下次GC,对于没有标记的对象则会放到一个单向的空闲列表free_list里面,这样当新建对象需要分配内存时我们就可以从free_list里面取出合适的分块。

 

复制算法说明:

“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,持续复制长生存期的对象则导致效率降低。

分代收集算法说明:

GC 分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。

“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

 

G1收集器说明:

与CMS收集器相比G1收集器有以下特点:

1、空间整合,G1收集器采用标记整理算法,不会产生内存空间碎片。分配大对象时不会因为无法找到连续空间而提前触发下一次GC。

2、可预测停顿,这是G1的另一大优势,降低停顿时间是G1和CMS的共同关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时 Java(RTSJ)的垃圾收集器的特征了。

使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region 的集合。

G1的新生代收集跟 ParNew 类似,当新生代占用达到一定比例的时候,开始出发收集。和 CMS 类似,G1 收集器收集老年代对象会有短暂停顿。

 

内存模型与回收策略:

手绘一张图详细介绍GC回收机制

Java 堆(Java Heap)是JVM所管理的内存中最大的一块,堆又是垃圾收集器管理的主要区域,Java 堆主要分为2个区域-年轻代与老年代,其中年轻代又分 Eden 区和 Survivor 区,其中 Survivor 区又分 From 和 To 2个区。

  • Eden 区

大多数情况下,对象会在新生代 Eden 区中进行分配,当 Eden 区没有足够空间进行分配时,虚拟机会发起一次 Minor GC,Minor GC 相比 Major GC 更频繁,回收速度也更快。 通过 Minor GC 之后,Eden 会被清空,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区(若 From 区不够,则直接进入 Old 区)。

  • Survivor 区

Survivor 区相当于是 Eden 区和 Old 区的一个缓冲,类似于我们交通灯中的黄灯。Survivor 又分为2个区,一个是 From 区,一个是 To 区。每次执行 Minor GC,会将 Eden 区和 From 存活的对象放到 Survivor 的 To 区(如果 To 区不够,则直接进入 Old 区)。Survivor 的存在意义就是减少被送到老年代的对象,进而减少 Major GC 的发生。Survivor 的预筛选保证,只有经历16次 Minor GC 还能在新生代中存活的对象,才会被送到老年代。

  • Old 区

老年代占据着2/3的堆内存空间,只有在 Major GC 的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。内存越大,STW 的时间也越长,所以内存也不仅仅是越大就越好。由于复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以老年代这里采用的是标记——整理算法。

 

 

 

 

 

 

 

 

 

 

 

上一篇:什么是ColorMatrix


下一篇:�《给产品经理讲JVM》:垃圾收集器