java的四种引用

强引用

java默认的引用方式就是强引用,只要栈帧没有被弹出并且引用还指向着对象,对象就不会被 GC 清除,通过把引用置为 null ,可以使得 GC 清除对象

软引用

出现内存不足的情况下会被 GC 回收,这里注意不是出现 GC 就会回收,而是出现内存不足才会回收

//JVM 参数设置 -XX:+PrintGCDetails -Xms10m -Xmx10m
public static void main(String[] args) {
	SoftReference<byte[]> sr = new SoftReference<byte[]>(new byte[1024*1024*2]);
    ArrayList<byte[]> arrayList = new ArrayList<byte[]>();
    for(int i = 1; i <= 10; i++) {
        arrayList.add(new byte[1024*1024]);
        System.out.println("第" + i + "次创建对象,弱引用对象:" + sr.get());
    }
}

软引用样例GC打印信息

代码中创建了一个 2mb 的 byte 数组

在第 6 次先 ArrayList 中添加对象时,由于内存空间不够发生了 GC ,GC 完毕后可以看到内存占用由 7766k 变成了 5693k 从减少了 2073k ,稍稍大于 2mb,可以确定 byte 数组被清除了,再打印对象为null

弱引用

只要出现了 GC ,即无论是否出现内存不足,对象都会被回收

public static void main(String[] args) {
    WeakReference<byte[]> weakReference = new WeakReference<byte[]>(new byte[1024*1024*10]);
    System.out.println("before GC : " + weakReference.get());
    System.gc();
    System.out.println("after GC : " + weakReference.get());
}

弱引用样例输出信息

我们显式调用 GC 后被对象被清除

注意要是用下面方式使用弱引用,就起不到弱引用的效果

public static void main(String[] args) {
    byte[] bytes = new byte[1024*1024*10];
    WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes);
    System.out.println("before GC : " + weakReference.get());
    System.gc();
    System.out.println("after GC : " + weakReference.get());
}

因为还存在其他引用指向对象(软引用也如此)

虚引用

虚引用和没有引用一样,只不过在 GC 回收对象时,会将对象添加到一个队列中,可以让程序观察到对象被回收了

用处,在 NIO 中存在一个 DirectByteBuffer 类,该类使用的是堆外内存,由于内存不在 JVM 堆空间中,如果 DirectByteBuffer 对象不在指向该空间,且不回收堆外空间就会出现内存泄漏的情况,所以需要使用一个机制来通知 JVM 把堆外空间给回收了(马士兵视频中的例子)

PhantomReference 需要与 ReferenceQueue 配合使用,当 PhantomReference 关联的对象被回收时, PhantomReference 就会被添加到 ReferenceQueue 队列中

下图中例子模拟了当对象被回收时通知到程序进行后续工作

package cn.arc.java;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.LinkedList;
import java.util.List;

/**
 * @author arc3102
 * @date 2021/5/5 18:50
 */
public class PhantomReferenceTest {
    private static final List<byte[]> LIST = new LinkedList<byte[]>();
    private static final ReferenceQueue<byte[]> QUEUE = new ReferenceQueue<byte[]>();

    public static void main(String[] args) {
        final PhantomReference<byte[]> phantomReference = new PhantomReference<byte[]>(new byte[1024 * 1024], QUEUE);
        new Thread(new Runnable() {
            public void run() {
                while(true) {
                    LIST.add(new byte[1024*1024]);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        Thread.currentThread().interrupt();
                    }
                    System.out.println(phantomReference.get());
                }
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                while(true) {
                    Reference<? extends byte[]> poll = QUEUE.poll();
                    if(poll != null) {
                        System.out.println("--- 虚引用对象被 JVM 回收了 --- " + poll);
                    }
                }
            }
        }).start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

虚引用样例输出

我们把 byte 数组改为对象,重写 finalize 方法,再跑一下

package cn.arc.java;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.LinkedList;
import java.util.List;

/**
 * @author arc3102
 * @date 2021/5/5 22:30
 */
public class PhantomReferenceTest2 {

    static class M{
        private byte[] bytes;

        M() {
          this.bytes = new byte[1024*1024];
        }

        @Override
        protected void finalize() throws Throwable {
            System.out.println("我是M的finalize()");
            super.finalize();
        }
    }

    private static final List<byte[]> LIST = new LinkedList<byte[]>();
    private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<M>();


    public static void main(String[] args) {
        final PhantomReference<M> phantomReference = new PhantomReference<M>(new M(), QUEUE);
        new Thread(new Runnable() {
            public void run() {
                while(true) {
                    LIST.add(new byte[1024*1024]);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        Thread.currentThread().interrupt();
                    }
                    System.out.println(phantomReference.get());
                }
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                while(true) {
                    Reference<? extends M> poll = QUEUE.poll();
                    if(poll != null) {
                        System.out.println("--- 虚引用对象被 JVM 回收了 --- " + poll);
                    }
                }
            }
        }).start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

虚引用样例输出2

从结果中可以看出在对象的 finalize 方法完成后 phantomreference 才会被添加到队列中,而且两者相隔的时间还不短

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值