一、判断对象的存活
在堆里面存放着几乎所有的对象实例,垃圾回收器在对对进行回收前,要做的事情就是确定这些对象中哪些还是“存活”着,哪些已经“死去”(死去代表着不可能再被任何途径使用得对象了)
1.1、引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
如下代码所示:
String p = new String("abc");

当我们上述代码的方法结束了,或者我们手动将其引用去除
p = null;

上述就是我们的引用计数算法,其优点为:快、方便、实现简单,但是它又有缺点,就是对象相互引用时,很难判断对象是否该回收。

上述示意图中就存在对象间的相互引用,造成了环形数据,即使该环形数据没有任何使用的地方,但是因为它们的引用不为0,所以他们永远不会被回收,也就会造成“内存泄漏”的问题。
1.2、可达性分析算法
通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
作为GC Roots的对象主要包括下面几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。

1.3、finalize方法
即使通过可达性分析算法判断不可达的对象,也不是“非死不可”,它还会处于“缓刑”阶段,真正要宣告一个对象死亡,需要经过两次标记过程,一次是没有找到与 GCRoots 的引用链,它将被第一次标记。随后进行一次筛选(如果对象重写了finalize方法),我们可以在 finalize 中去拯救。
但是一般不建议使用该方法,因为该方法优先级较低,无法保证其调用顺序,其不确定性大;另外该方法只会被系统自动调用一次,第二次则不会被调用。一般会建议在try-finally中进行处理。
1.4、方法区回收
Java虚拟机规范中是不要求堆方法区实现垃圾收集的,因为在方法区进行垃圾收集的效率较低,一般方法区垃圾收集的主要内容是:废弃常量和无用的类。
如一个字符串“abc”进入了常量池中,如果当前系统中没有任何一个String对象叫做“abc”的,即没有任何String对象引用处常量池中的该字符串,这是“abc”常量就可以被回收。
如上述判断一个常量是否可进行回收相对简单,而对于无用的类进行垃圾收集的条件就比较苛刻了,需要满足如下几点:
- 该类所有的实例都已经被回收,也就是堆中不存在该类的任何实例。
- 加载该类的 ClassLoader 已经被回收。
- 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
- 需要保证虚拟机的相关参数的设置,其中
-Xnoclassgc用于控制是否对类进行回收。
二、对象的引用
无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象的引用链是否可达,判断对象是否存活都与“引用” 有关。在JDK1.2之后,Java中对引用的概念进行了扩充,如下:
2.1、强引用
就是指程序代码之中普遍存在的,类似Object ojb = new Object()这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
2.2、软引用
用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发现内存溢出异常之前,将会把这些对象列入回收范围之中进行第二次回收。如果注册回收还没有足够的内存,才会抛出内存溢出的异常。在JDK1.2之后,提供了SoftReference类来实现软引用。
例如,一个程序用来处理用户提供的图片。如果将所有图片读入内存,这样虽然可以很快的打开图片,但内存空间使用巨大,一些使用较少的图片浪费内存空间,需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来,虽然内存占用较少,但一些经常使用的图片每次打开都要访问磁盘,代价巨大。这个时候就可以用软引用构建缓存。
2.3、弱引用
用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。
注意: 软引用 SoftReference 和弱引用 WeakReference,可以用在内存资源紧张的情况下以及创建不是很重要的数据缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。
实际运用:ThreadLocal、WeakHashMap
2.4、虚引用
也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK1.2之后,提供了PhantomReference类来实现虚引用。
本文详细解读了Java中对象存活判断的引用计数算法、可达性分析方法,以及各种引用类型(强引用、软引用、弱引用和虚引用)。深入理解内存回收机制,包括finalize方法和方法区垃圾回收,以及不同引用类型的实战应用。
479

被折叠的 条评论
为什么被折叠?



