Java的四种引用类型-强软弱虚

强引用

  • 在Java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到 JVM 也不会回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。因此强引用是造成 Java 内存泄漏的主要原因之 一。
    在这里插入图片描述

案例演示

public class Obj {
    /**
     * 在GC垃圾回收时被调用
     */
    @Override
    protected void finalize() throws Throwable {
        System.out.println("==>> finalize 被打印,说明该对象被回收");
    }
}
/**
 * 强引用
 */
public class T01NormalReference {
    public static void main(String[] args) throws IOException {
        Obj o = new Obj();
        o = null;
        System.gc(); // DisableExplicitGC

        System.in.read();
    }
}
// 结果输出
// ==>> finalize 被打印,说明该对象被回收

软引用

  • 软引用需要用 SoftReference 类来实现,对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中,用来实现内存敏感的缓存。
    在这里插入图片描述

案例演示

  • 设置运行的最大堆内存
    在这里插入图片描述
  • 演示1
/**
 * 软引用
 */
public class T02SoftReference {
    private static final int SIZE_5_M = 1024 * 1024 * 5;
    private static final int SIZE_6_M = 1024 * 1024 * 6;

    public static void main(String[] args) {
        SoftReference<byte[]> sr = new SoftReference<>(new byte[SIZE_5_M]);
        System.out.println(sr.get());
        System.gc();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        System.out.println(sr.get());

        // 再分配一个数组,堆内存将装不下,这时候系统会发生GC
        // 先回收一次,如果不够,会把软引用干掉
        byte[] b = new byte[SIZE_6_M];
        System.out.println(sr.get());
    }
}
  • 结果输出
[B@61bbe9ba
[B@61bbe9ba
null       // 说明被垃圾回收了
  • 演示2
public class Obj {
    private Integer id;
    private String name;

    public Obj() {
    }

    public Obj(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "[id=" + id + ", name=" + name + "]";
    }

    /**
     * 在GC垃圾回收时被调用
     *
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        System.out.println("==>> finalize 被打印,说明该对象" + toString() + "被回收");
    }
}

/**
 * 软引用
 */
public class T02SoftReference {
    public static void main(String[] args) {
        SoftReference<Obj> sr = new SoftReference<>(new Obj(1, "Tom"));
        Runnable r1 = () -> {
            while (true) {
                if (sr.get() == null) {
                    System.out.println("==>> " + sr.get());
                    break;
                } else {
                    System.out.println("==>> " + sr.get());
                }
            }
        };
        Runnable r2 = () -> {
            List<String> list = new ArrayList<>();
            int i = 100;
            while (true) {
                list.add((i++) + "");
            }
        };

        new Thread(r1).start();
        new Thread(r2).start();
    }
}
  • 结果输出
...
==>> [id=1, name=Tom]
==>> [id=1, name=Tom]
==>> [id=1, name=Tom]
==>> [id=1, name=Tom]
==>> null
==>> finalize 被打印,说明该对象[id=1, name=Tom]被回收
Exception in thread "Thread-1" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.util.Arrays.copyOfRange(Arrays.java:3664)
	at java.lang.String.<init>(String.java:207)
	at java.lang.StringBuilder.toString(StringBuilder.java:407)
	at com.yw.T02SoftReference.lambda$main$1(T02SoftReference.java:49)
	at com.yw.T02SoftReference$$Lambda$2/2129789493.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:748)

小结

  • 当发生GC时,虚拟机可能会回收SoftReference对象所指向的软引用,是否被回收取决于该软引用是否是新创建或近期使用过。
  • 在虚拟机抛出OutOfMemoryError之前,所有软引用对象都会被回收。
  • 只要一个软引用对象由一个强引用指向,那么即使是OutOfMemoryError时,也不会被回收。

软引用主要用在缓存,比如你想使用一张大的图片。如果将这张图片用强引用存在内存里面,那么会比较占地方,其它线程都不能使用;但如果不存起来,每次都要去进行加载,效率会很低。这个时候,你就可以使用软引用,在内存充足时可以缓存,在内存不足时又可以及时把内存空间释放,兼顾了时间和空间的效率。

弱引用

  • 弱引用需要用 WeakReference 类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够,总会回收该对象占用的内存
    在这里插入图片描述

案例演示

/**
 * 弱引用
 */
public class T03WeakReference {
    public static void main(String[] args) {
        WeakReference<Obj> wr = new WeakReference<>(new Obj());

        System.out.println(wr.get());
        System.gc();
        System.out.println(wr.get());

        ThreadLocal<Obj> tl = new ThreadLocal<>();
        tl.set(new Obj());
        tl.remove(); // !!! 注意用完了要remove,防止内存泄漏
    }
}

在这里插入图片描述
在这里插入图片描述

虚引用

  • 虚引用需要 PhantomReference 类来实现,它不能单独使用,必须和引用队列联合使用。虚引用的主要作用是跟踪对象被垃圾回收的状态,应用于对堆外内存的管理
    在这里插入图片描述

案例演示

public class T04PhantomReference {
    private static final List<Object> LIST = new LinkedList<>();
    private static final ReferenceQueue<Obj> QUEUE = new ReferenceQueue<>();

    public static void main(String[] args) {
        PhantomReference<Obj> pr = new PhantomReference<>(new Obj(), QUEUE);

        new Thread(() -> {
           while (true) {
               LIST.add(new byte[1024 * 1024]);
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   System.out.println(e.getMessage());
                   Thread.currentThread().interrupt();
               }
               System.out.println(pr.get());
           }
        }).start();

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

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }
    }
}
  • 结果输出:
null
null
==>> finalize 被打印,说明该对象[id=null, name=null]被回收
null
null
null
...
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

讲文明的喜羊羊拒绝pua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值