垃圾回收——对象已死?

版权声明:码字不易,转载请标明出处 https://blog.csdn.net/qq_36906627/article/details/82390490

  在了解了JVM 的内存区域分布后,我们知道几乎所有的实例对象都在堆上存放,垃圾收集器在对堆进行垃圾回收之前,第一件事就是判断哪些对象是“存活”,哪些已经“死去”(即不可能再被任何途径使用的对象)。那么JVM 该如何判断呢?

一、 谈谈引用

  在回收“死亡”对象之前,我们需要对对象的“死亡”下一个定义——当一个对象不存在任何引用的时候,称为死亡。所以在介绍接下来来的内容之前我们需要谈谈引用。

在JDK 1.2 之前,Java 中引用的定义很传统:
如果reference 类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存地址代表一个引用。

这样的定义很纯粹,但不全面。这种定义下一个对象就只存在有引用和没有引用两种状态。在前面的定义下,大量的对象会被判断为“死亡”,这样对一些“食之无味弃之可惜”的对象来说显得太过无情(亦或说太过频繁的GC 不是我们希望看到的情况)。就像人有惰性,事到不可不为才为之一样,我们希望描述这样的对象:当内存空间足够时,保留;当GC 过后内存空间不足时,抛弃。
  在JDK1.2 之后Java 对引用的概念进行了扩充,将引用分为:强引用(Strong reference)、软引用(Soft reference)、弱引用(Weak reference)、虚引(Phantom reference)用四种,引用强度依次递减。

  • 强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 ps:强引用其实也就是我们平时A a = new A()这个意思。

  • 软引用用来描述一些非必须对象。在系统将要发生内存泄露异常之前,将会把这些对象列入回收范围,如果还不足,才报异常。软引用主要应用在用户实现缓存。JDK1.2 之后提供了SoftReference 类来实现软引用。
    Object obj = new Object();
    SoftReference sf = new SoftReference(obj);

  • 弱引用也是用来描述非必须对象,它的程度比软引用更弱,只能存活到下一次GC 之前。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾。JDK1.2 之后提供了WeakReference 类来实现弱引用。
    Object obj = new Object();
    WeakReference wf = new WeakReference(obj);

  • 虚引用,也称幽灵引用和幻影引用。它根本取不到对象的值,它的唯一目的就是能在这个对象被GC 时收到一个系统通知。虚引用主要用于检测对象是否已经从内存中删除。JDK1.2 之后提供了PhantomReference 类来实现虚引用。
    Object obj = new Object();
    PhantomReference pf = new PhantomReference(obj);

关于引用的详细介绍参考这个博客:Java四种引用包括强引用,软引用,弱引用,虚引用。
因为对象是否能够被回收靠它是否被引用来判断,所以这里花了一些篇幅来介绍引用。

二、 判断对象是否死亡的算法

  在介绍完引用的概念后,我们开始看看判断对象是否死亡的算法 ,你会知道判定对象是否存活都与“引用”有关。

(1) 引用计数算法

什么是引用计数法?

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

这个算法很优秀,但是很难解决对象相互引用的问题,在主流的Java 虚拟机中没有采用这个算法。

public class Test{
    public Object instance = null;
    ...
}
Test A = new Test();
Test B = new Test();
A.instance = B;
B.instance = A;
A = null;
B = null;
System.gc();

这里两个对象的引用都不为0 ,但是会被GC。

(2) 可达性分析算法

可达性算法应用在目前主流的程序语言中(Java、c#等),这个算法的主要思路就是通过一系列的称为“GC Root” 的对象作为起点,从这些节点开始往下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Root 没有任何引用链相连,则证明此对象是不可用的。
这里写图片描述
如图object7、object8、object9就是可被回收对象。
在Java 语言中可被作为GC Root 的对象包含:

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

三、回收方法区

永久代的垃圾回收主要回收两个部分的内容:废弃常量和无用的类。回收废弃常量和回收Java 堆中的类非常类似。主要是回收无用的类。要回收无用的类必须满足3个条件:

  • 该类所有的实例都已经被回收,也就是Java 堆中不存在该类的任何实例
  • 加载该类的ClassLoader 已经被回收
  • 该类对应的java.lang.Class 对象在任何地方都没有引用,无法通过反射访问该类

虚拟机可以对满足上述3个条件的无用类进行回收,“可以”不代表一定要回收。只在在大量使用反射、动态代理、GCLib 等ByteCode 框架、动态生成JSP 以及OSGi 这类频繁自定义ClassLoader 的场景都需要虚拟机具备卸载的功能,以保证永久代不会溢出。

猜你喜欢

转载自blog.csdn.net/qq_36906627/article/details/82390490
今日推荐