JAVA GC寻找垃圾算法|8月更文挑战

393 阅读3分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

寻找垃圾算法.png

1 引用计数法

引用计数法(Reference Count)会给对象中添加一个引用计数器,每当有一个地方引用它的时候,计数器的值就 +1 ,当引用失效时,计数器值就 -1 ,计数器的值为 0 的对象不可能在被使用,这个时候就可以判定这个对象是垃圾。

引用计数法.png

当图中的数值变成0时,这个时候使用引用计数算法就可以判定它是垃圾了,但是引用计数法不能解决一个问题,就是当对象是循环引用的时候,计数器值都不为0,这个时候引用计数器无法通知GC收集器来回收他们,如下图所示:

引用计数法-问题.png

这个时候就需要使用到我们的根可达算法。

2 可达性分析

根可达算法(Root Searching)的意思是说从根上开始搜索,当一个程序启动后,马上需要的那些个对象就叫做根对象,所谓的根可达算法就是首先找到根对象,然后跟着这根线一直往外找到那些有用的。常见的GC roots如下:

  • 线程栈变量: 线程里面会有线程栈和main栈帧,从这个main() 里面开始的这些对象都是我们的根对象
  • 静态变量: 一个class 它有一个静态的变量,load到内存之后马上就得对静态变量进行初始化,所以静态变量到的对象这个叫做根对象
  • 常量池: 如果你这个class会用到其他的class的那些个类的对象,这些就是根对象
  • JNI: 如果我们调用了 C和C++ 写的那些本地方法所用到的那些个类或者对象

根可达算法.png

图中的 object5 和object6 虽然他们之间互相引用了,但是从根找不到它,所以就是垃圾,而object8没有任何引用自然而然也是垃圾,其他的Object对象都有可以从根找到的,所以是有用的,不会被垃圾回收掉。

GC Root

GC Roots 是一组必须活跃的引用。用通俗的话来说,就是程序接下来通过直接引用或者间接引用,能够访问到的潜在被使用的对象。GC Roots 包括:

  • Java 线程中,当前所有正在被调用的方法的引用类型参数、局部变量、临时值等。也就是与我们栈帧相关的各种引用
  • 所有当前被加载的 Java 类
  • Java 类的引用类型静态变量
  • 运行时常量池里的引用类型常量(String 或 Class 类型)
  • JVM 内部数据结构的一些引用,比如 sun.jvm.hotspot.memory.Universe 类
  • 用于同步的监控对象,比如调用了对象的 wait() 方法
  • JNI handles,包括 global handles 和 local handles

这些 GC Roots 大体可以分为三大类:

  • 活动线程相关的各种引用
  • 类的静态变量的引用
  • JNI 引用

有两个注意点:

  • 我们这里说的是活跃的引用,而不是对象,对象是不能作为 GC Roots 的
  • GC 过程是找出所有活对象,并把其余空间认定为“无用”;而不是找出所有死掉的对象,并回收它们占用的空间。所以,哪怕 JVM 的堆非常的大,基于 tracing 的 GC 方式,回收速度也会非常快