Java中有四种引用类型:
- 强引用
- 软引用
- 虚引用
- 弱引用
在 Java 中一切都被视为了对象,但是我们操作的标识符实际上是对象的一个引用(reference)。Java中,引用都被存储在了栈中,而实例对象几乎都被存在了Java堆中。
引用可以单独存在而不指向任何对象:
String str;
Integer i;
通过将一个引用指向一个对象后,就可以通过这个引用来操作对象了:
str = new String("Hello");
System.out.println(str);
强引用
Java中默认的声明就是强引用:
String str = new String("Hello");
Object obj = new Object(); //只要obj还指向Object对象,Object对象就不会被回收
obj = null; //手动置null
如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象
软引用
软引用是用来描述一些非必需但仍有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。这种特性常常被用来实现缓存技术,比如网页缓存,图片缓存等。
这里举一个软引用被gc回收的例子(参考自马士兵):
import java.lang.ref.SoftReference;
public class MyTest {
public static void main(String[] args) {
SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024 * 10]);
System.out.println(softReference.get()); // 通过get获取byte,并打印byte数组地址
System.gc(); // 主动调用gc进行垃圾回收,测试软引用对象是否被回收
try {
Thread.sleep(500); // 给gc线程一些时间去发现软引用(实际上这里不会被回收)
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(softReference.get());
// 这里需要设置程序运行的堆内存大小空间 为3MB
byte[] reference = new byte[1024 * 1024 * 15];
System.out.println(softReference.get());
System.out.println(reference);
}
}
软引用中的真实数据会被放在SoftReference
中,而我们程序中的softReference
并不能直接获取到软引用的数据,而应当使用get()
来获取里面的数据。为测试这个案例需要在IDEA中设置运行时堆的内存最大为30MB(马士兵的视频中为20MB,而在我测试过程中20MB就会报错)。
测试结果表明,当堆内存不够时,软引用会被垃圾回收,而强引用则不会。
运行结果:
[B@10f87f48
[B@10f87f48
null
[B@b4c966a
弱引用
弱引用的引用强度比软引用要更弱一些,具有弱引用的对象拥有的生命周期更短暂,无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象。在JDK1.2之后,用java.lang.ref.WeakReference
来表示弱引用。
虚引用
虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用 PhantomReference
类来表示
参考: