如何判断对象可否回收

引用计数法

注:这种方法并未被JVM采用!

思路是:每当一个对象被引用时,就将其引用计数值加1,当值为 0 时,就表示该对象不被引用,可以被垃圾收集器回收。

引用计数法存在一个弊端,两个对象互相循环引用时,两个对象的引用计数都为1,导致两个对象都无法被释放。

可达性分析算法

JVM 中的垃圾回收器通过可达性分析来探索所有存活的对象。

垃圾回收器通过扫描堆中的对象,看能否沿着 GC Root 对象为起点的引用链找到该对象,如果找不到,则表示可以回收。

可以作为 GC Root 的对象:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中 JNI(即一般说的Native方法)引用的对象

jmap命令获取到的堆快照可以导入Eclipse Memory Analyzer 工具进行GCroot的分析。

四(五)种引用

  • 强引用
    只有所有 GC Roots 对象不通过强引用引用该对象,该对象才能被垃圾回收。
  • 软引用(SoftReference)
    仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象。可以配合引用队列来释放软引用自身。
  • 弱引用(WeakReference)
    仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象。可以配合引用队列来释放弱引用自身
  • 虚引用(PhantomReference)
    必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存。
  • 终结器引用(FinalReference)
    无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象。

垃圾回收算法

标记清除 Mark Sweep

  • 速度较快
  • 会产生内存碎片

标记整理 Mark Compact

  • 速度慢
  • 没有内存碎片

复制

  • 不会有内存碎片
  • 需要占用两倍内存空间

分代垃圾回收

  • 新创建的对象首先分配在 eden 区
  • 新生代空间不足时,触发MINOR GCeden 区from 区存活的对象使用 copy 复制到to区中,存活的对象年龄加一,然后交换 from、to区
  • MINOR GC会引发stop the world,暂停其他线程,等垃圾回收结束后,恢复用户线程运行
  • 当幸存区对象的寿命超过阈值时,会晋升到老年代,最大的寿命是 15(4bit)
  • 当老年代空间不足时,会先触发MINOR GC,如果空间仍然不足,那么就触发FULL GC ,停止的时间更长!

附:相关 JVM 参数

含义 参数
堆初始大小 -Xms
堆最大大小 -Xmx 或 -XX:MaxHeapSize=size
新生代大小 -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size )
幸存区比例(动态) -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy
幸存区比例 -XX:SurvivorRatio=ratio
晋升阈值 -XX:MaxTenuringThreshold=threshold
晋升详情 -XX:+PrintTenuringDistribution
GC详情 -XX:+PrintGCDetails -verbose:gc
FullGC 前 MinorGC -XX:+ScavengeBeforeFullGC

未完