Day14 JVM 2 GC 垃圾回收机制
uwupu 啦啦啦啦啦

GC:垃圾回收机制

GC作用区:Heap 和 方法区;

JVM在进行GC时,大部分时候,回收的都是新生代。

类型

  • 轻GC(GC),大部分时候清理新生代,偶尔幸存区
  • 重GC(全局GC),

如何判断哪些对象需要被回收

Java 垃圾回收判断哪些对象需要被回收有两种方法:引用计数法可达性分析算法

可达性分析算法

通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

GC Roots

GC Roots,tracking GC的根集合,是一组必须活跃的引用

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

  1. 虚拟机栈中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等;
  2. 方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量;
  3. 方法区中常量引用的对象,譬如字符串常量池里的引用;
  4. 本地方法栈中JNI(Native方法)引用的对象
  5. Java虚拟机内部的引用,如基本数据类型对应的CLass对象,一些常驻的异常对象(比如NullPointException,OutOfMemoryError)等,还有系统类加载器;
  6. 所有被同步锁(synchronized关键字)持有的对象;
  7. 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等;
  8. 根据用户所选用的垃圾收集器以及当前回收的内存区域不同,“临时性”地加入的其他对象。

Tracing GC

根本思路

给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可达到的)对象就判定为存活,其余对象(没有被遍历到的)就自然判定为死亡。

注意:tracing GC的本质是通过找出所有活对象来吧其余空间认定为“无用”,而不是找出所有死掉的对象并回收他们占用的空间。

GC Roots引用例子

image

在上图中,reference1、reference2、reference3都是GC Roots;

其中:

  • reference1 -> 对象实例1
  • reference2 -> 对象实例2
  • reference3 -> 对象实例4 -> 对象实例6

可以得出对象实例1、2、4、6都具有GC Roots可达性、也就是存活对象,不会被GC回收的对象;

而对于3、5是GC Roots不可达对象,是需要被GC回收的对象。

不可达的对象处理

不可达的对象将暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:

  1. 当对象变成GC Roots不可达时,GC会判断该对象是否有重写finalize方法,若没有,则直接回收,若有,进入第二步

  2. 若对象执行过finalize方法,则进行回收;若对象未执行过finalize方法,将其放入F-Queue队列,虚拟机会稍后建立一个低优先级线程Finalizer去执行该队列中对象的finalize方法,执行finalize方法结束后,GC会再次判断该对象是否科大,若不可达,则进行回收,若可达,则对象列为存活对象。

    ![未被GC Roots引用的对象](Day14-JVM-2/未被GC Roots引用的对象.svg)

    这里的执行表示虚拟机会执行该方法,但不承诺等待它运行结束。

算法

分类

标记清除法,标记整理,复制算法,引用计数器。

引用计数法

image

复制算法

复制 + 清除垃圾

复制算法主要体现在新生区的幸存区。

过程

为表明算法过程,先将幸存区分成两个区:A区和B区

  1. 新生对象会被分配到A区的未使用内存中,当A区内存满了,就把A区存活对象复制到B区;
  2. 然后清理A区所有对象;
  3. 新生对象会被分配到B区未使用的内存中,当B区满了,就把B区存活对象复制到A区;
  4. 清理B区所有对象;
  5. 按照上面的过程循环

在Java里,一般将空的内存称为to区,当前正在使用的内存称为from区。(第二步结束时候,A为to区,B为from区;在第4步结束时候,B成为to区,A为from区)

特点

  • 好处:没有内存碎片
  • 坏处:浪费了内存空间(to永远为空)

复制算法最佳使用场景:对象存活度较低的时候。

标记清除算法

每经历一次GC,会进行可达性分析,标记GC Roots可达的对象,清理没有被标记的对象。

特点

优点:不需要额外的空间。

缺点:两次扫描,严重浪费时间,会产生内存碎片。

image

标记压缩算法

标记清除算法的优化版本,每经历几次GC,会进行一次扫描,然后将对象移动到内存的一端,防止内存碎片产生。

GC总结

内存效率最高(时间复杂度):复制算法 > 标记清除算法 > 标记压缩算法

内存整齐度:复制算法 > 标记压缩算法 = 标记清除算法

内存利用率:标记压缩算法 = 标记清除算法 > 复制算法

没有最好的算法,只有最合适的算法。

GC:分代收集算法

结论

年轻代:存活率低 —>复制算法

老年代:区域大,存活率高 —> 标记清除(内存碎片不是太多) + 标记压缩 混合实现

书籍

《深入理解JVM》

其他

从文章中删除的内容

对象的标记

对象由两种状态,涉及到两类状态空间:

  • 一个是终结动态空间F = {unfinalized,finalizable,finalized}
  • 一个是可达状态空间R = {reachable,finalizer-reachable,unreachable}。

各状态含义如下:

  • unfinalized:GC未准备执行其finalize方法,新建对象会先进入此状态;

  • finalizable:表示可以执行finalizable方法,GC已检测到对象不可达;

  • finalized:表示GC已对对象执行过finalize方法;

  • reachable:表示GC Roots引用可达;

  • finalizer-reachable(f-reachable):表示不是reachable,但通过某个finalizable对象可达;

  • unreachable:对象不可通过上面两种途经可达;

执行过程:

  1. 新建对象首先处于{reachable,unfinalized}状态;
  2. 若引用关系消失,从reachable变为f-reachable或unreachable状态;
  3. 若JVM….Delete
 评论