深入理解Java虚拟机—垃圾回收 上

Java内存运行时区域中的程序计数器、虚拟机栈和本地方法栈
这个三个区域都是随线程而生,随线程而灭,也就不需要过多考虑回收的问题
因为方法或线程结束时,内存自然就跟着回收了

而Java堆和方法区则不一样,这部分内存的分配和回收都是动态的
垃圾收集器所关注的就是这部分内存

判断对象的存亡

垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象有哪些还活着
哪些已经死去(即不可能再被任何途径使用的对象)

1.引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1
当引用失效时,计数器值就减1
任何时刻计数器都为0的对象就是不可能再被使用的
但是,Java语言中没有选用引用计数法来管理内存
其中最主要的原因是它很难解决对象之间相互循环引用的问题

2. 根搜索算法

Java是使用根搜索(GC Roots Tracing)算法判定对象是否存活的
这个算法的基本思路是通过一系列的名为“GC Roots”的对象作为起始点
从这些节点向下搜索,搜索所走过的路径称为引用链
当一个对象到GC Roots没有任何引用链相连时,则证明此对象时不可用的

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

  1. 虚拟机栈中的引用对象
  2. 方法区中的类静态属性引用的对象
  3. 方法区中的常量引用的对象
  4. 本地方法栈中JNI(即一般说的Native方法)的引用的对象

引用

在JDK1.2之前,Java中的引用的定义很传统
如果Reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这个内存代表着一个引用
一个对象在这种定义下只有被引用或者没有被引用这两种状态

在JDK1.2之后,Java对引用的概念进行了扩充
将引用分为强引用、软引用、弱引用和虚引用
强引用
类似Object obj = new Object();,只要强引用存在,垃圾收集器永远不会回收掉被引用的对象
软引用
描述一些还有用,但非必需的对象
对于软引用关联的对象,在系统将要发生内存溢出异常之前
将会把这些对象列进回收范围之内并进行第二次回收
弱引用
描述非必需对象,但强度弱与软引用
当垃圾收集器工作时,无论当前内存是否足够
都会回收掉只被弱引用关联的对象
虚引用
也称为幽灵引用或者幻影引用,是最弱的引用关系
一个对象是否有虚引用的存在,完全不会对其生存时间构成影响
也无法通过虚引用获得对象实例
为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时受到一个系统通知

"对象的缓刑"

在根搜索算法中不可达的对象,也并非是“非死不可”的
这时候它们处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:
如果一个对象在进行根搜索后发现没有与GC Roots相连接的引用链
那么它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法
finalize方法定义在Object类中,其定义如下:

/**
    * Called by the garbage collector on an object when garbage collection
    * determines that there are no more references to the object.
    * A subclass overrides the {@code finalize} method to dispose of
    * system resources or to perform other cleanup.
**/
protected void finalize() throws Throwable { }

对该函数的注释内容是比较多的,上面的只是一小部分,注释内容翻译如下:
当垃圾回收器确定某个对象不再有任何的引用时,该对象的finalize方法被调用
而子类可以通过重写这个方法来处理系统资源或者执行其它的清理

由于Object类中的finalize方法是空的,所以如果子类不重写finalize方法
那么垃圾收集器在调用finalize方法时不会执行任何内容

OK,之前说到筛选的条件是此对象是否有必要执行finalize方法
如果这个对象没有重写finalize方法,就相当于执行了个空而已
如果这个对象已经执行过了finalize方法,那么就没有必要再次执行
对上述的这两种情况来说,对象都是"必死无疑"的,即经历过第一次标记后,基本上就是逃不出一个"死"字

那么如果一个对象被判定为有必要执行finalize方法
那么这个对象将会被放置在一个名为F-Queue的队列之中,虚拟机将会自动创建线程去执行这个队列中的内容
然后,GC将对F-Queue中的对象进行第二次小规模的标记
如果对象的finalize方法中与引用链中的任何一个对象建立了关联
那么该对象将会被移除出“即将回收”的集合 当然,如果没有建立起关联,那么该对象"死亡"

在书中作者建议:尽量避免使用它来拯救对象,因为它运行代价高昂,不确定性大
若是想在对象销毁前关闭外部资源,可以使用try-finally
可以完全忘掉Java语言中还有这个方法的存在

回收方法区

Java虚拟机规范中明确说过不要求虚拟机在方法区实现垃圾收集
但实际上虚拟机在方法区是实现了垃圾收集的

方法区的实现不同,对应的垃圾收集内容也不同
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类
元空间的垃圾收集主要回收无用的类

类需要同时满足下面3个条件才能算是“无用的类”:

  1. 该类的所有实例都已经被回收
  2. 加载该类的ClassLoader已经被回收
  3. 该类对应的java.lang.Class对象没有在任何地方被引用

虚拟机可以对满足上述3个条件的无用类进行回收
以上内容摘自于 周志明—《深入理解Java虚拟机-JVM高级特性与最佳实践》

原文地址:https://www.cnblogs.com/ASE265/p/12768787.html