一、继承结构
1.1 四大引用的继承关系
在 Java 中一共有四种引用类型,分别是强引用、弱引用、软引用和虚引用,其中,我们常用的是强引用,而其他三种引用都需要引入特定的 java.lang.ref 才能使用,他们的继承结构如下:
引用继承结构图
一般只有强引用是直接使用的,而其他的三种引用都需要配合引用队列(ReferenceQueue)来使用会更好。
Java 中变量的类型有两大类,值类型和引用类型,这里的引用类型就是指可以引用的对象。在对引用类型进行操作时,我们实际只是得到了它的内存地址,通过内存地址来访问这个对象,并对其进行相关的操作,这和 C++ 的指针极为相似,但不完全相同。C++ 有基础数据类型的指针,而 Java 没有基础数据类型的引用,取而代之的是值类型。
1.2 引用和指针的关系
这里拓展和延伸一下,具体解释 C++ 中的指针、引用和 Java 的引用之间的区别和联系。
C++ 的指针
C++ 中的指针指向对象的内存地址,在参数传递时,以指针传递时只会将对象的内存地址传过去,在函数或方法内部再对地址进行访问以达到传参的目的。
Java 的引用
Java 中的引用和 C++ 中的指针类似,但是也不完全相同。Java 中的引用没有基础类型的引用,只能是引用类型,因此相比于 C++,没有 int 类型变量的地址一说,Java 只能得到引用类型的地址,无法得到基本数据类型的内存地址。
C++ 的引用
C++ 中也是有引用的,不过 C++ 的引用和指针以及 Java 的引用不同,它是变量或者对象的别名,和拷贝不一样,它是直接对原本的数据进行操作的。
二、四大引用方式
2.1 强引用(Reference)
使用强引用不需要引入任何包,Java 的引用方式默认就是强引用。
我们一般使用的引用都是强引用,当内存不足时,JVM 开始回收垃圾,但对于强引用的对象,JVM 不会对其进行回收,就算出现了 OOM(OutOfMemory),JVM 也不会对其进行回收,也就是说,强引用的对象是不会被 JVM 给回收的。这可以防止一些资源在程序运行的时候被回收了,当然,有利也有弊,当一些资源过大时,它们会占用大量的内存,甚至出现了 OOM 时,JVM 依然不会对其回收,这可能导致程序出现问题。在某些情况下,这会造成程序的内存泄露。
2.2 软引用(SoftReference)
使用软引用需要引入 java.lang.ref.SoftReference。
软引用不像强引用那么绝对,当内存不足时,JVM 就会尝试回收垃圾,而软引用的对象在这个时候就会被回收。不过,若没有达到内存不足的情况,JVM 不会回收软引用的对象。软引用的这个特性非常好,可以说它就是 Java 自动管理内存的标配,为什么这么说呢?我们知道,Java 是可以自动管理内存的,但在程序的运行过程中,JVM 是无法自动判断某些内存空间的,而这部分空间如果都是强引用对象且占用了很大的内存,甚至导致出现了 OOM,那么程序就出问题。这个时候,程序员就应该将其设定为软引用,告诉 JVM,这个对象在内存不足的时候是可以释放的。图片资源的缓存,网页资源的缓存中就大量使用了软引用。
2.3 弱引用(WeakReference)
使用弱引用需要引入 java.lang.ref.WeakReference。
弱引用,是一种比软引用还要“弱”的引用方式。当 Java 的垃圾回收机制开始运行时,弱引用的对象就会立刻被回收,无论内存是否充足。弱引用一般使用不多。
2.4 虚引用(PhantomReference)
使用虚引用需要引入 java.lang.ref.PhantomReference。
虚引用,顾名思义,其引用是虚的,并非真实的引用,因此可以认为这个对象没有任何引用,这个对象也无法通过虚引用被访问。虚引用对象在任何时候都可能被回收。虚引用可以理解为给这个对象打了个标记,程序员可以从中知道,这个对象要被回收或者已经被回收,虚引用的存在只是象征着这个对象曾经存在过。
三、总结
以上四种引用方式可以总结为下表:
引用方式 | 内存不足时 | 垃圾回收机制启动时 | 其余情况 |
强引用 | 否 | 否 | 否 |
软引用 | 是 | 否 | 否 |
弱引用 | 是 | 是 | 否 |
虚引用 | 可能 | 可能 | 可能 |
在一般的程序中,弱引用和虚引用很少会被用到,强引用天天都在用就不必我说了,在有些情况下(如资源文件很大,但并不是全部要在同一时刻使用)我们会使用软引用来代替原来的强引用,一是可以避免某些情况下产生的 OOM,此外,它可以减少程序使用的内存,加速程序的运行。