Java中的强软弱虚引用

Java中的强软弱虚引用

强引用

强引用内存示意图:
在这里插入图片描述

Java中最普遍的引用,比如有个A类,我们使用new关键字去创建A的实例的时候
A a = new A(); 栈中变量a指向堆中A的实例这个引用就是强引用。

public class TestForceReference {
    public static void main(String[] args) {
        A a = new A(); // 创建出A的实例对象,变量a指向堆中的A对象实例
        System.out.println(a); // 打印a对象
        System.gc(); // 主动触发GC
        System.out.println(a); // 打印a对象
        a = null; // 断开变量a指向堆中的引用
        System.gc(); // 触发GC
        System.out.println(a); // 打印a对象
        System.in.read(); // 阻塞主线程,给GC线程留时间回收对象
    }
}

看下程序的输出结果:

com.zs.reference.A@a14482
com.zs.reference.A@a14482
null

分析:
①第一个打印是创建出A的实例并输出到控制台com.zs.reference.A@a14482;
②触发GC后,由于堆中A的实例仍然被栈中变量a引用,所以这次GC并不能回收堆中A的实例对象,输出结果com.zs.reference.A@a14482;
③a = null 这个代码将栈中a指向A实例的引用断开,此时已经没有任何引用指向A的实例,再次触发GC,堆中A的实例已经被回收了,输出结果null。、
结论:
堆中被强引用的对象(不包括循环引用)不会被GC回收。

软引用

Java中可以使用SoftReference实现软引用,软引用内存示意图:
在这里插入图片描述

// 虚拟机启动参数 -Xmx30m -Xms30m
public class TestSoftwareReference {
    public static void main(String[] args) throws InterruptedException, IOException {
       // 这里创建了一个15M大小的byte[] 会被SoftReference中的某个成员变量所引用
        SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[1024 * 1024 * 15]); 
        // 通过get() 可以拿到这个字节数组 这里可以输出这个字节数组
        System.out.println(softReference.get());
        // 内存不紧张的时候触发GC
        System.gc();
        // 输出这个字节数组 这里也可以输出这个字节数组
        System.out.println(softReference.get());
        // 当再需要申请18M大小的内存时,堆中内存告急,这里会触发GC回收掉软引用
        byte[] bytes = new byte[1024 * 1024 * 18];
        // 已经被回收  输出null
        System.out.println(softReference.get());
        System.in.read(); // 阻塞主线程
    }
}

控制台输出结果:

[B@a14482
[B@a14482
null

结论:当JVM内存充裕的时候,GC并不会回收软引用;当JVM内存告急的时候,GC会回收掉软引用对象。

弱引用

Java中可以使用WeakReference实现弱引用,弱引用内存示意图:
在这里插入图片描述

public class TestWeakReference {
    public static void main(String[] args) throws IOException {
        // 创建一个Object() 会被弱引用weakReference中某个成员变量所引用,这个引用
        // 是java中的弱引用
        WeakReference<Object> weakReference = new WeakReference<Object>(new Object());
        // 也是通过get() 获取这个对象
        System.out.println(weakReference.get());
        // 主动触发GC
        System.gc();
        // 再调用get() 已经拿不到这个对象了
        System.out.println(weakReference.get());
        System.in.read();
    }
}

结论:弱引用的对象,只要发生了GC,就会被回收!

JDK中Threadlocale中的内存泄漏BUG后来的版本中就是用弱引用解决的

虚引用

Java中的虚引用比较特殊,用PhantomReference构建一个虚引用,而且必须搭配队列ReferenceQueue使用。虚引用指向的对象,通过其get()无法获取。当一个持有虚引用的对象将要被回收的时候,JVM起先并不会直接回收这个对象,而是先把此虚引用放到关联的队列中,其他线程通过轮训这个队列,如果队列中有这个引用,说明JVM马上就要回收这个对象了,需要在这个对象真正回收之前作出相关操作。
虚引用的内存布局:
在这里插入图片描述

// 虚拟机参数 -Xmx20M -Xms20M
public class TestPhantomReference {
    // 队列
    final static ReferenceQueue<A> queue = new ReferenceQueue();
    // 初始化一个集合
    final static List<byte[]> list = new ArrayList<byte[]>();
    public static void main(String[] args) throws IOException {
        // 构建一个虚引用指向new A();
        final PhantomReference<A> phantomReference = new PhantomReference<A>(new A(), queue);
        // 获取不到
        System.out.println(phantomReference.get());
        // 此线程每秒向list中添加1m的对象,终究会把堆空间占满
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    list.add(new byte[1024 * 1024]);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(phantomReference.get());
                }
            }
        }).start();
        // 这个线程一直在轮训这个队列,当内存不够触发GC的时候,GC线程会将这个虚引用放入
        // 队列中告诉JVM这个虚引用的对象将要被回收了
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    Reference<? extends A> poll = queue.poll();
                    if (poll!= null) {
                        System.out.println("回收");
                    }
                }
            }
        }).start();
        System.in.read();
    }
}

程序结果:

null
null
null
null
回收 // 在这里发生了GC
null
null

虚引用一般用作堆外内存的管理,JDK的NIO包中的ByteBuffer可以通过**ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);**直接分配堆外内存。

public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }

DirectByteBuffer中有个成员变量Cleaner

private final Cleaner cleaner;

这个Cleaner就继承了虚引用

public class Cleaner extends PhantomReference<Object>

当这个byteBuffer成为不可达,JVM在回收这个对象时候,发现其中有个虚引用指向堆外内存,会将这个虚引用放入关联的队列中,Cleaner可以在JVM级别实现对堆外内存的释放。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值