谈谈 Java 对象引用

这是我参与更文挑战的第29天,活动详情查看: 更文挑战

前言

在学习 Java 语言的时候,我们知道 Java 语言是强类型语言,它对每一种数据都定义了明确的具体数据类型,在内存中分配了不同大小的内存空间。其中基本类型共有 8 种,基本数据类型存储的是值,int 、char、float等,引用类型有 3 种,包括类(class)、接口(interface)和数组(array),引用类型存储的是地址。

所有的基本类型赋值都是按值传递,而引用类型是按引用传递。今天,我们就来谈谈 Java 的对象引用。

一、对象与引用

类比 c++ 中的指针指向某块内存空间,Java 则通过引用来完成对象地址的操作。我们通常在一定的内存空间(栈空间)来保存这种引用类型,再用堆空间中保存引用的对象,如下图:

image-20210629142531784

a 和 b 是不同的引用,引用了不同的对象 ”hello“ 和 ”hello world“ 。

我们也可以改变引用指向的对象,同时,一个对象也支持多个引用。

image-20210629145856158

二、四种引用

为什么 java 中会有四种引用类型? 我们知道 JVM 的GC 机制它有引用计数法,如果某个对象被引用了,则不可回收,在 JDK1.2 之前,一个对象只有 ”已被引用“ 和 ”未被引用“ 的两种状态,在内存不够用的时候,如果能回收部分被引用的内存,就可以达到性能上的提升的需求。

Java 为引用类型专门定义了一个类叫做 Reference 。在JDK1.2 之后,把对象的引用分为四种状态,即强引用、软引用、弱引用 和虚引用。这样的方式,可以更加灵活地控制对象的生命周期。其继承关系如图:

image-20210629120732937

这非常好的比喻概括了这四种引用的关系,借鉴一下:

1、强引用就像大老婆,关系很稳固。

2、软引用就像二老婆,随时有失宠的可能,但也有扶正的可能。

3、弱引用就像情人,关系不稳定,可能跟别人跑了。

4、虚引用就是梦中情人,只在梦里出现过。

​ — 来自《Java 核心技术36 讲》海怪哥哥 的留言

2.1 强引用 Strong Reference

Java 中的 强引用是指 能够通过 GC root 对象直接找到的对象,那么这个对象无论内存是否足够,都不会被回收。

强引用在程序代码中普遍存在,即时内存不足,JVM 宁愿抛出 OutOfMemory 错误也不会回收对象,比如下面这段:

String a="hello world";
Object o =new Object();
复制代码

不过,如果超出局部变量的作用范围,或者将强引用赋值为null,或调用clear()方法,那么就可以完成对象回收。

2.2 软引用 Soft Reference

软引用的意思是只有在内存不足的情况下,被引用的对象才会被回收。

注意,是内存不足才回收,内存充足的时候不会回收。软引用通常用来实现内存敏感的缓存,例如加载文章的时候,如果还有多余内存,就把图片加载出来,如果内存紧张,就不加载图片的缓存。

public class test {
    public static void main(String[] args) {
        SoftReference<String> str = new SoftReference<String>("hello world");
        System.out.println(str.get());
        System.gc();
        System.out.println(str.get());
    }
}
复制代码

2.3 弱引用 weak Reference

弱引用的等级更低,无论内存死否足够,只要开启进行垃圾回收的时候,被弱引用的对象都会被回收

public class test {
    public static void main(String[] args) {
        WeakReference<String> str = new WeakReference<>(new String("hello world"));
        System.out.println(str.get());
        System.gc();
        System.out.println(str.get());
    }
}
复制代码

输出结果:

hello world
null
复制代码

2.4 虚引用 Phantom Reference

虚引用是最弱的一种引用,它随时可能被回收,且我们不能通过它访问引用对象,

通常,为一个对象设置虚引用的唯一目的就在于跟踪垃圾回收过程,例如,如果这个对象被回收时候能够收到一个系统的通知。

查看该类的源码,发现它只有一个构造函数和一个get() 方法,而且它的get()方法仅仅是返回一个null 。

public class PhantomReference<T> extends Reference<T> {

   
    public T get() {
        return null;
    }

    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

复制代码

虚引用还必须和 引用队列(ReferenceQueue)联合使用,当GC开始回收对象的时候,判断到该对象仅存在虚引用,就会在回收对象前,把这个虚引用加入到关联的引用队列中。因此,我们就可以在引用队列中查看某个引用是否在队列中存在,然后就可以得出该对象被垃圾回收了,发出一个系统通知。

总结:

引用类型 回收时间 用途
强引用 GC时不回收,可以把该对象置为null等回收 项目中广泛使用
软引用 GC时内存不足时才回收 内存敏感的缓存中使用
弱引用 GC时回收 对象缓存使用
虚引用 GC时回收 配合引用队列使用,垃圾回收发送通知

参考资料:

  • 《Java 核心技术 36 讲》
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享