浅谈 Java 四种引用类型

好久没有写技术博客了,虽然写了一些面经(微信公众号:卡戎),但技术才是根本。今天就谈一谈 Java 对象的四种引用方法吧。

StrongReference(强引用)

A a = new A();

这就是强引用,也是最常见的引用方式。如果对象是一个强引用,那么 JVM 绝对不会回收他,绝对到哪种程度呢?宁愿牺牲自己抛出 OutOfMemoryError 挂掉程序,也不会去回收。正如「在我死之前,我不允许任何人伤害你」。JVM 做到了对强引用的承诺,完美的诠释了这句话的含义。

SoftReference(软引用)

SoftReference softReference = new SoftReference<>(new String("Hello SoftReference"));

这是一个典型的软引用,是不是看着有点奇怪?如果 JVM 内存充足,无论发生多少次 GC,软引用指向的对象都不会被回收,如果 JVM 内存满了,那么软引用就请退位让贤吧。颇有一丝「达则兼济天下,穷则独善其身」的味道。软引用没有被回收,就可以一直被访问,可用于缓存。

WeakReference(弱引用)

WeakReference weakReference = new WeakReference<>(new String("Hello WeakReference"));

弱引用看着是不是和软引用很接近呢?弱引用和软引用唯一的区别就是:只要执行了垃圾回收,弱引用的对象就会被回收,这个和 JVM 内存满不满没有关系。正所谓「一万年太久,只争朝夕」,弱引用的生命周期最长就是两次 GC 的间隔时间。如果抬杠说程序直接挂掉没有 GC 右上角关闭出门左转。

PhantomReference(虚引用,幻引用)

ReferenceQueue referenceQueue = new ReferenceQueue<>();  
PhantomReference phantomReference = new PhantomReference<>(new String("Hello PhantomReference"), referenceQueue);

虚引用和上面两种引用有一点点小区别,多了一个依赖队列。虚引用并不会决定对象的生命周期,有他没他都一个样,无法通过虚引用获取对象。

实战演习

说了那么多,来实战一波吧,JVM 启动参数 -Xms5M -Xmx5M -Xmn5M

//软引用
SoftReference softReference = new SoftReference<>(new String("Hello SoftReference"));
//弱引用
WeakReference weakReference = new WeakReference<>(new String("Hello WeakReference"));
ReferenceQueue referenceQueue = new ReferenceQueue<>();
//虚引用
PhantomReference phantomReference = new PhantomReference<>(new String("Hello PhantomReference"), referenceQueue);
System.out.println("GC 之前:softReference:" + softReference.get() + ",weakReference:" + weakReference.get() + ",phantomReference:" + phantomReference.get());
System.gc();
//休眠 1s 给 JVM 时间执行 GC 
Thread.sleep(1000);
System.out.println("第一次 GC 之后:softReference:" + softReference.get() + ",weakReference:" + weakReference.get() + ",phantomReference:" + phantomReference.get());
//模拟内存占用
SoftReference[] softArr = new SoftReference[5];
softArr[0] = new SoftReference(new byte[1024 * 1024 * 2]);
softArr[1] = new SoftReference(new byte[1024 * 1024 * 1]);
softArr[2] = new SoftReference(new byte[1024 * 1024 * 2]);
System.out.println("内存满了之后:softReference:" + softReference.get() + ",weakReference:" + weakReference.get() + ",phantomReference:" + phantomReference.get());

运行结果如图

总结

上述讲解都建立在一个基础上:对象有且只有当前一个引用。当下面这段代码执行的时候,无论多少次都 GC 不掉当前对象。

String s = new String("Hello world");
WeakReference weakReference = new WeakReference<>(s);
System.gc();
Thread.sleep(10 * 1000);
System.out.println(weakReference.get());

运行结果:Hello world。难道上面讲的都是错的?当然不是,分析如下。

JVM 是采用的根可达分析策略来标记垃圾回收,如果一个对象被多个引用持有,按照引用强度最强的那个计算,「Hello world」对象明显被 s 持有,属于强引用,运行结果是对的,符合上面所讲述的引用规则。

后记

我记得刚第一次遇见 SoftReference 是大二刚学习 Android 的时候,在一个学长写的一段代码里面看见的。去搜索,有一篇博客告诉我,说这个类很多搞了两三年 Java 开发的都不一定知道。 当时心里暗自吐槽,博主见识浅薄,我才学习两个三月就遇见了。但自此之后,我再也没有实战的时候遇见遇过了,现在想想当初那个博主说的很有道理,连类似技术博文都很少见。

上面讲的三个类都继承自 Reference ,都只有构造方法,没有任何自己实现或者重写。我真的惊呆了,只能通过代码注释了解这三种类的作用。

PS:关注微信公众号:卡戎,免费领取 Java 资源。


欢迎大家关注我的微信公众号:卡戎。
浅谈 Java 四种引用类型_第1张图片

如有错误,欢迎留言!

你可能感兴趣的:(java,基础)