Java垃圾回收主要在哪里进行?相关算法 – 三郎君的日常

面试 · 2022年7月22日 0

Java垃圾回收主要在哪里进行?相关算法

Java垃圾回收是针对于内存的。

Java内存运行时区域大概分了三部分

其中PC寄存器、java虚拟机栈、本地方法栈3个区域是所有线程独有的一块区域,随线程而生,随线程而灭。栈中的栈帧随着方法的进入和退出而有条不紊地执行着入栈和出栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,因此这几个区域的内存分配和回收都具备确定性,在这几个区域内就不需要过多考虑回收的问题,因为方法结束或者线程结束,内存自然就跟随着回收了。

而Java堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收是动态的,垃圾收集关注的是这部分的内存。

垃圾检测(标记)算法

什么样的对象才是垃圾?

如果一个对象没有被其他对象所引用该对象就是无用的,此对象就被称为垃圾,其占用的内存也就要被销毁。那么自然而然的就引出了我们的第二个问题,判断对象为垃圾的算法都有哪些?

标记垃圾的算法

Java中标记垃圾的算法主要有两种,引用计数法可达性分析算法。我们首先来介绍引用计数法。

引用计数法

引用计数法就是给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的,可以当做垃圾收集。这种方法实现起来很简单而且优缺点都很明显。

优点 执行效率高,程序执行受影响较小

缺点 无法检测出循环引用的情况,导致内存泄露

什么是循环引用呢?

假设有A和B两个对象之间互相引用,也就是说A对象中的一个属性是B,B中的一个属性时A,这种情况下由于他们的相互引用


可达性分析

可达性分析基本思路是把所有引用的对象想象成一棵树,从树的根结点 GC Roots 出发,持续遍历找出所有连接的树枝对象,这些对象则被称为“可达”对象,或称“存活”对象。不能到达的则被可回收对象。

下面这张图就是可达性分析的描述:

a394121f8bac2349adaca52289a46a5d.png

我们发现,GC Roots 本身是一个出发点,也就是说我们每次进行可达性分析的时候都要从这个初始点出发。换句话说,初始点我们一定是可达的。那么,Java 里有哪些对象可以作为GC Roots呢?主要有以下四种:

虚拟机栈(帧栈中的本地变量表)中引用的对象。

方法区中静态属性引用的对象。

方法区中常量引用的对象。

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

总之,JVM在做垃圾回收的时候,会检查堆中的所有对象是否会被这些根集对象引用,不能够被引用的对象就会被垃圾收集器回收。

垃圾收集(回收)算法

已经能够确定那些对象可以被视为垃圾了。下面我们可以分析一下,如何去回收这些垃圾,同样的,有一系列算法。首先我们定义一个规则确定那些是垃圾、存活对象、空白空间

0226c2899121e469804b92f2ef54d8b2.png

垃圾收集算法有四种:

标记-清除算法

标记-整理算法

复制算法

分代收集算法

95c3896fa9e5807542fe2174e7f4b799.png