深度理解JVM----垃圾回收策略

以下内容部分转载于: CS-Notes

垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之

后就会消失,因此不需要对这三个区域进行垃圾回收。

引用计数法(Reference Counting)

为对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。

在两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。

正是因为循环引用的存在,因此 Java 虚拟机不使用引用计数算法

例如

public class Test {

    public Object instance = null;

    public static void main(String[] args) {
        Test a = new Test();
        Test b = new Test();
        a.instance = b;
        b.instance = a;
        a = null;
        b = null;
        doSomething();
    }
}

在上述代码中,a 与 b 引用的对象实例互相持有了对象的引用,因此当我们把对 a 对象与 b 对象的引用去除之后,由于两个对象还存在互相之间的

引用,导致两个 Test 对象无法被回收。


可达性分析算法 (Reachability Analysis)

以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。

Java 虚拟机使用该算法来判断对象是否可被回收,GC Roots 一般包含以下内容:

  • 虚拟机栈中局部变量表中引用的对象
  • 本地方法栈中 JNI 中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中的常量引用的对象

简单来说,只要和GC Roots没有关联的对象,不管它与别的对象是否有关联,都是可以回收的


引用类型(Reference)

无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。

Java 提供了四种强度不同的引用类型。

强引用(Strong Reference)

只要强引用的关联还在,垃圾收集器永远不会回收掉引用的对象

Object obj = new Object()
软引用(Soft Reference)

在系统将要发生内存溢出异常之前,将会把这些对象列入回收范围之中,进行二次回收

如果此时内存不够,才会抛出内存溢出异常

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;  // 使对象只被软引用关联
弱引用(Weak Reference)

在每一次垃圾回收时,无论当前内存是否足够,都会回收掉弱引用关联的对象

弱引用的生命周期只能存活到下一次垃圾回收前

Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
虚引用(Phantom Reference)

虚引用也称幽灵引用和幻影引用,是否有虚引用的存在,完全不会对其生存时间够成影响,也无法通过虚引用来取得一个对象实例

为一个对象设置虚引用的唯一目的是能在这个对象被回收时收到一个系统通知

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, null);
obj = null;

回收前的自我救赎(了解即可)

即使在可达性分析算法中不可达的对象,也并非“非死不可”,这时候他们暂时处于“缓刑”状态

如果重写了finalize()方法,虚拟机将执行finalize()方法,如果重新和GC roots关联上,那么这个对象将逃脱死亡

但是需要注意的是,如果没有重写finalize()方法,或者已经执行过这个方法,那么虚拟机将视为“没必要执行”的状态,直接进行回收

PS :这个方法是Java刚诞生时为了使C/C++程序员更容易接受所做的一个妥协,它运行代价高昂,不确定性大,无法保证各个对象的调用顺序

在这里只是了解即可,不推荐大家使用,忘记这个方法即可


方法区的回收

方法区在HotSpot虚拟机中又称为永久代,存储已被虚拟机加载的类,常量,静态变量,即时编译器编译后的代码等数据

对于这个区域进行回收,“性价比”一般较低,主要回收目标为废弃常量无用的类

废弃常量

我们以回收常量池中字面量的回收为例

假设一个字符串**“abc”已经进去的常量池,但是当前系统中,没有任何一个地方引用了“abc”**这个对象,此时发生回收的有必要的,因为这是一个废

弃常量,不被系统的任何一个地方使用

无用的类

判断一个类是否为无用的类的条件,比判定是否为无用常量苛刻的多,必须满足一下三个条件才能算是**“无用的类”**

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

虚拟机对满足上述3个条件的无用类进行回收,这里说的是仅仅是“可以”,并不是对象不使用了就必然会回收

在大量使用反射,动态代理,CGLibByteCode框架,动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值