闯关JVM垃圾回收机制 -- 引入篇

229 阅读10分钟

1. 引入

C/C++中,程序员负责对象的创建和销毁。如果程序员忽略了无用对象的销毁,可能在某个时刻,程序因无法获得足够的内存来创建新对象造成程序异常终止,最终导致内存溢出错误。

但在Java中,程序员不需要关心那些不再使用的对象。垃圾回收器会销毁这些对象帮助我们管理内存。程序员将控制内存的权力交给了JVM,所以一旦出现内存泄漏、溢出方面的问题,如果不了解JVM内部工作机制,排查、修复问题将会是一项艰难的工作。所以,认识并深入理解JVM垃圾回收机制是很必要的。

在开启核心知识的学习之前,让我们先试着解答三个问题。

Q1: 什么是垃圾回收机制?

垃圾回收机制Garbage CollectionGC)是Java程序执行自动内存管理的过程,主要是针对存放在内的对象进行管理。Java程序编译为字节码,可以在Java虚拟机(JVM)上运行。当Java程序在JVM上运行时,对象在上创建,堆是专门为程序分配的一部分内存。最终,一些对象将不再被需要。垃圾收集器找到这些未使用的对象并删除它们以释放内存。

Q2: 为什么垃圾回收只关注堆区域?

image.png

程序计数器虚拟机栈,以及本地方法栈都是线程私有,随着线程生灭;针对而言,栈中的栈帧随着方法的进入和退出进行出入栈操作,每一个栈帧占用的内存基本在类结构确定下来就是已知的。

以上区域内存的分配和回收具备确定性,无需考虑回收问题,当方法/线程结束时,内存自然跟着回收。

针对于方法区/元空间而言,只有在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收是动态的,所以说,垃圾回收机制需要关心的只有方法区/元空间

方法区或元空间的垃圾收集主要回收两部分内容:废弃常量和无用的类(主要是针对于类相关的变量/方法,也就是static修饰的)。《Java虚拟机规范》中提到过可以不要求虚拟机在方法区中实现垃圾收集,此区域进行垃圾收集的性价比一般比较低。

所以针对于垃圾回收机制,我们主要关心内存的垃圾回收。

Q3:什么时候进行垃圾回收?

回收的时机是由垃圾回收器Garbage Collector)决定,垃圾回收器的具体策略和时机会根据不同的实现有差异。一般来说,以下几种情况会触发GC

  • 对象不再被引用:当一个对象不再被引用时,它就成为垃圾对象。垃圾回收器会周期性地扫描内存,找出这些不再被引用的对象,并将它们标记为可回收的。这是最常见的回收时机。
  • 内存不足:当系统中的可用内存接近极限时,垃圾回收器会被触发来回收一些不再使用的对象,以释放内存空间。这种情况下的回收被称为压力驱动的回收。
  • 程序的显式调用:某些情况下,程序可以显式调用垃圾回收器进行垃圾回收,如:在程序中使用System.gc()可以建议垃圾回收器进行回收操作,但不能保证立即回收。
  • 程序空闲时:当程序处于空闲状态时,即没有活动的线程在运行,垃圾回收器可以利用这段时间来回收内存。例如,在 Java 中,当所有线程都处于等待状态或者没有活动时,垃圾回收器可能会被触发。

对上述三个基本问题有了大概了解,我们将开启对垃圾回收机制的学习。学习垃圾回收机制,主要是围绕三个核心问题进行展开:

  • 什么样的对象被认为是垃圾

  • 如何判断一个对象是否为垃圾

  • 如何进行垃圾回收

以上问题将会在以下三篇文章中做出解答:

  • 《闯关JVM垃圾回收机制 -- 引入篇》 主要解答了什么样的对象被认为是垃圾;
  • 《垃圾回收的“方法论” -- 垃圾回收算法》 主要介绍了常见的垃圾回收算法;
  • 《垃圾回收的“实践者” -- 垃圾回收器》 主要介绍了几种经典的垃圾回收器。

对学习垃圾回收机制的整个框架有了大致了解,接下来就开启引入篇的学习。究竟什么样的对象被认定为垃圾呢?

2. 什么是垃圾?

2.1 定义

垃圾:不被引用的对象(运行过程中没有任何指针指向的对象)

2.2 引用的分类

JDK1.2 版之前对象只有被引用未被引用之分,在此之后,Java对引用概念的进行了扩充,提供了四个级别的引用,按照引用强度依次排序为:

  • 强引用Strong Reference
  • 软引用Soft Reference
  • 弱引用Weak Reference
  • 虚引用Phantom Reference

2.2.1 强引用(StrongReference

强引用:通过new指令创建出来的对象都属于强引用类型,类似Object obj = new Object()这类的引用;即使在内存紧张的情况下,强引用对象不会被垃圾回收器回收,只有当强引用被显式解除时,对象才会被回收。

2.2.2 软引用(Soft Reference

软引用:当内存不足时,垃圾回收器尽量保留软引用对象,只有在内存真正不足时才会回收。软引用通常用于实现内存敏感的高速缓存。在JDK1.2版本之后提供了java.lang.ref.SoftReference类来实现软引用。

2.2.3 弱引用(Weak Reference

弱引用:用来描述非必需对象的,被弱引用关联的对象 只能生存到下一次垃圾收集发生之前 。当垃圾收集器工作时,无论内存是否足够,都会回收掉只被弱引用关联的对象。当对象只被弱引用引用时,垃圾回收器可以在下一次回收时将其回收。弱引用通常用于实现缓存、观察者模式等场景。在 JDK1.2 版之后提供了 java.lang.ref.WeakReference 类来实现弱引用

2.2.4 虚引用(Phantom Reference

虚引用:也称为幽灵引用或者幻影引用,是最弱的一种引用关系。主要作用是跟踪对象被垃圾回收的状态,无法通过虚引用来获取对象实例。虚引用通常用于管理直接内存。在 JDK1.2 版之后提供了 java.lang.ref.PhantomReference 类来实现虚引用。

image.png

3. 如何判断一个对象是否为垃圾?

回收垃圾前一定要先找到垃圾,并将其标记(给已死的对象打上记号,方便GC),那么如何判断一个对象是否该标记,或者说如何判断一个对象是否为垃圾呢

3.1 引用计数法(Reference Counting)

给对象添加一个引用计数器,对象被引用计数器的值 加1不被引用计数器的值减1;任何时刻计数器为0的对象就被认为是不可能再使用的。

  • 优势:实现简单,判断效率高,回收没有延迟性。
  • 劣势
    • 需要额外存储计数器;
    • 运行期间需要维护计数器,带来额外开销;
    • 存在循环引用(如:AB相互引用)的问题。

3.2 可达性分析算法(Reachability Analysis

3.2.1 基本思路

通过一系列称为GC Roots的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为引用链Reference Chain),如果某个对象到 GC Roots 间没有任何引用链相连,或者用图论的话来说就是从 GC Roots 到这个对象不可达时,则证明此对象是不可能再被使用的。

总结:定义一个存放的都是活跃引用GC Root集合,集合中存放的内容经过特殊筛选;算法思想就是:从GCRoot集合出发,所有可达的对象都是存活的对象,不可达的对象都是垃圾

image.png

3.2.2 如何理解对象可达?

对象可达:双方存在直接或者间接的引用关系,根可达或GC Roots可达就是指:对象到GC Roots存在直接或间接的引用关系。

3.2.3 GC Roots是什么?

垃圾回收时,JVM要先找到所有的GC Roots,这个过程称作枚举根节点GC Roots是一种特殊的对象,而且是JVM确定当前绝对不能被回收的对象,只有找到这种对象,后面的搜寻过程才有意义,不能被回收的对象所依赖的其他对象肯定也不能回收。

哪些对象可以成为GC Roots呢?

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象,如:各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。

  • 方法区中类静态属性引用的对象,如:Java类的引用类型静态变量。

  • 方法区中常量引用的对象,如:字符串常量池(String Table)里的引用。

  • 本地方法栈中JNI(即通常所说的Native方法)引用的对象

  • Java虚拟机内部的引用,如:基本数据类型对应的 Class 对象,一些常驻的异常对象(比如 NullPointExcepitonOutOfMemoryError)等,还有系统类加载器。

  • 所有被同步锁(synchronized关键字)持有的对象

除了这些固定的 GC Roots 集合以外,根据用户所选用的垃圾收集器以及当前回收的内存区域不同,还可以有其他对象临时性地加入,共同构成完整 GC Roots集合

3.3 两次标记过程

对象被回收之前/标记之后,该对象的finalize()方法会被调用,finalize()方法主要是执行一些清理工作,例如关闭文件、释放资源等。

被标记的对象在执行finalize()后,可能会出现的一种情况:被标记的对象重新与引用链上某一个对象建立关联,但该对象已经被标记为垃圾,所以可能会造成标记了不该回收的对象这种情况。

针对上述情景,JVM提供了两次标记过程,通过两次检验来以避免上述问题的发生。

  • 第一次标记:可达性分析后标记不在引用链中的对象;
  • 第二次标记:判断该对象有没有实现finalize()方法,如果没有实现就直接判断这个对象可回收。如果实现了就会先放在一个队列中,并由虚拟机建立的一个低优先级的线程去执行它,随后就会进行第二次的小规模标记,在这次被标记的对象就会真正的被回收了。

参考资料