JVM垃圾回收之—强软弱虚引用

1.强软弱虚引用介绍

JDK1.2之后,显示支持 强、软、弱、虚 四种类型引用

(1)Strong Reference 强引用,只要强引用还存在:JVM 就不会回收这种对象

所谓强引用就是我们最常使用的,类似于 User u = new  User()

那么就说 u 持有 User 对象的强引用:且只要 User 对象还被u持有,那么就不会被回收

 

(2)Soft Reference 软引用,在系统内存溢出之前,会把软引用持有的对象回收

如果这时内存还是不够,才会发生内存溢出。像很多组件就设计使用软引用来持有缓存对象。

 

(3)Weak Reference 弱引用,被弱引用关联的对象只能存活到下一次JVM垃圾回收之前

 

(4)Phantom Reference 虚引用,最弱的一种引用关系,你甚至都不能使用get方法

从虚引用获取其关联的对象

 

2.JVM 如何回收强引用类型对象

package com.yli.jvm;

/**
 * Strong Reference 强引用:由 new Objec() 形式创建的对象 <br>
 * 如果谁持有该对象的引用,那么就是强引用,比如:<br>
 * User u = new User(); 那么就说 u 持有 User 对象的强引用<br>
 * 所以很好理解强引用:他就是我们最常使用的引用<br>
 * 
 * 那么强引用对象在什么时候才会被 JVM 回收呢? <br>
 * 当没有任何引用到达该对象时,JVM会自行决定的何时回收该对象<br>
 * 
 * 比如上述 User 对象,如果我们设置 u = null; <br>
 * 那么在这种最简单情况下:我们说没有任何引用可到达之前创建的 User 对象<br>
 * 这时 JVM 会对 User 对象进行第一次标记,并将标记后的对象放入一个低优先级队列<br>
 * 再JVM下次GC之前又会进行第二次标记,如果对象没有实现finalize方法<br>
 * 那么 JVM 就认定 User 对象已经可以回收了!<br>
 * 
 * 从上面的描述中发现什么特点了么:就是 finalize 方法<br>
 * 如果你再这个方法里面 拯救 User 对象,那意味着User可以复活的...
 * 
 * 
 * 新生代10M,初始堆和最大堆都是20M <br>
 * -Xmn10M -Xms20M -Xmx20M
 * 
 * @author yli
 */
public class TestStrongReference {

    private static int _8MB = 8 * 1024 * 1024;
    private static TestStrongReference test = new TestStrongReference();

    public static void main(String[] args) {
        // testOOM();

        testSaveSelf();
    }

    /**
     * 这个方法用来演示 GC 过程<br>
     * 即如果实现了 finalize 方法,在GC过程中会调用一次<br>
     * 在这次调用过程中对象有机会复活自己,如果没有复活成功<br>
     * 那么就没机会了,因为finalize方法只会执行一次!
     */
    private static void testSaveSelf() {
        try {

            // 设置为 null,再运行 gc 方法
            // 可以明显发现 finalize 方法被执行
            test = null;
            System.gc();
            // 延迟500ms 等待 gc 完成
            Thread.sleep(500);

            // 成功复活自己
            if (test == null) {
                System.out.println("test 对象已死!");
            } else {
                System.out.println("test 对象活着!");
            }

            // 重复来一次上述过程
            test = null;
            // 这一次 finalize 不会执行了,因为已经执行过一次了!
            System.gc();
            // 延迟500ms 等待 gc 完成
            Thread.sleep(500);

            // 复活失败
            if (test == null) {
                System.out.println("test 对象已死!");
            } else {
                System.out.println("test 对象活着!");
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 简单测试堆内存溢出 <br>
     * 这里虽然也会执行GC,并且会运行 finalize 方法<br>
     * 但明显没有啥内存可回收:因为obj1持有强引用且不释放<br>
     * 所以再次创建 obj2 时会内存溢出
     * 
     */
    private static void testOOM() {
        // 消耗 8M 内存
        byte[] obj1 = new byte[_8MB];
        System.out.println("---->内存还不会溢出!");

        try {
            test = null;
            System.gc();
            // 延迟500ms 等待 gc
            Thread.sleep(500);

            byte[] obj2 = new byte[_8MB];

        } catch (OutOfMemoryError err) {
            System.out.println("---->内存会溢出!");
            err.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 如果JVM进行GC操作,肯定会打印如下消息
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("-------->JVM 回收内存!");

        // 拯救 test 对象
        test = this;
    }

}


3.JVM 如何回收软引用对象

package com.yli.jvm;

import java.lang.ref.SoftReference;

/**
 * 软引用SoftReference:引用关系比强引用低一个等级<br>
 * 当系统内存不足可能导致内存溢出之前,JVM会把软引用关联的对象回收<br>
 * 如果这时系统内存仍然不足才会发生内存溢出!<br>
 * 
 * 新生代5M,初始堆和最大堆都为10M(即不允许堆内存扩展)<br>
 * -Xmn5M -Xms10M -Xmx10M
 * 
 * @author yli
 */
public class TestSoftReference {

    private static int _8MB = 8 * 1024 * 1024;

    public static void main(String[] args) throws InterruptedException {
        // 消耗8MB内存空间
        SoftArticle article = new SoftArticle(new byte[_8MB]);
        SoftReference<SoftArticle> softRef = new SoftReference<SoftArticle>(article);

        // article 是 new SoftUser()创建的对象,因此他持有SoftUser的【强引用】
        // 此处设置为 article = null非常重要
        // 这保证了刚才创建的 SoftUser对象只被 【softRef】持有引用
        // 并且是持有:【软引用】
        article = null;

        // 测试:由 【软引用】 关联的对象是否还活着!
        testAliveOrDead(softRef);

        // 重新创建一个强引用对象:需再次消耗8MB内存空间
        article = new SoftArticle(new byte[_8MB]);

        // 再次测试:由 【软引用】 关联的对象是否还活着!
        testAliveOrDead(softRef);
    }

    // 从软引用的get方法就能判断对象是否已经被回收!
    private static void testAliveOrDead(SoftReference<SoftArticle> softRef) {
        if (null == softRef.get()) {
            System.out.println("额...我已经被JVM回收...已经死亡!");
        } else {
            System.out.println("额...我还活着!");
        }
    }
}

class SoftArticle {
    private byte[] content;

    public SoftArticle(byte[] content) {
        this.content = content;
    }

    public byte[] getContent() {
        return this.content;
    }
}


4.JVM 如何回收弱引用对象

package com.yli.jvm;

import java.lang.ref.WeakReference;

/**
 * 弱引用:对象只能存活到下一次垃圾回收之前<br>
 *
 * @author yli
 */
public class TestWeakReference {

    public static int count = 0;
    private static TestWeakReference tref = new TestWeakReference();

    // user 持有 WeakUser 对象的引用
    private static WeakUser user = new WeakUser("ali");

    public static void main(String[] args) throws InterruptedException {

        /**
         * 如果一个对象只被WeakReference关联<br>
         * 那么该对象只能存活到下一次垃圾回收之前
         */
        // user 对象被 weakRef 即一个弱引用关联
        WeakReference<WeakUser> weakRef = new WeakReference<WeakUser>(user);

        // 这样保证 只有 weakRef 持有 WeakUser 对象的引用
        user = null;
        System.out.println("user--->" + weakRef.get());
        
        count++;
        tref = null;
        System.gc();
        Thread.sleep(500);
        System.out.println("user--->" + weakRef.get());
        
        count++;
        tref = new TestWeakReference();
        tref = null;
        System.gc();
        Thread.sleep(500);
        
        System.out.println("user--->" + weakRef.get());

 
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println(String.format("第%s次内存回收!", count));
    }
}

class WeakUser {
    private String name;

    public WeakUser(String n) {
        this.name = n;
    }

    public String toString() {
        return String.format("{name:%s}", name);
    }
}


5.JVM 如何回收虚引用对象

package com.yli.jvm;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

/**
 * 虚引用:最弱的引用关系<br>
 * 使用虚引用,你可能只是想在垃圾回收之前收到通知!<br>
 * 
 * JVM 参数设置为查看GC过程详细信息<br>
 * -XX:+PrintGCDetails
 * 
 * @author yli
 */
public class TestPhantomReference {

    public static void main(String[] args) {
        /**
         * 这里只演示:一旦使用虚引用关联该对象,就无法从虚引用获取该对象了!
         */
        PhantomUser user = new PhantomUser();
        ReferenceQueue<PhantomUser> queue = new ReferenceQueue<PhantomUser>();
        PhantomReference<PhantomUser> pref = new PhantomReference<PhantomUser>(user, queue);

        // 打印为 null
        System.out.println(pref.get());
    }
}

class PhantomUser {
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值