Java 四种引用类型介绍和演示

一、前言

Java有四种引用类型:强引用、软引用、弱引用、虚引用,Java中默认声明的就是强引用,我们用的最多的也是强引用,这里会对每种引用关系做说明和演示。

  • 提前准备一个User类演示时需要使用
public class User {
    
    
    private Long id;
}

二、Java 四种引用类型

强引用(StrongReference)

Java中默认声明的就是强引用,比如:

User user = new User(); // 创建一个User对象,使用强引用
System.gc(); // 手动触发GC
if(user == null){
    
    
    System.out.println("user对象已经被GC回收    " + user);
}else {
    
    
    System.out.println("user对象没有被GC回收    " + user);
}
user = null; // 手动置null 取消引用

只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足时,JVM也会直接抛出OutOfMemoryError,不会去回收。如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null,这样一来,JVM就可以适时的回收对象。

软引用(SoftReference)

如果一个对象只具有软引用,在内存空间足够的时候,就算手动触发垃圾回收,也是不会回收它的;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

要模拟出这个效果需要在启动参数中设置堆内存大小:

-Xmx1M -Xms1M
SoftReference<User> softRef = new SoftReference(new User()); // 软引用
System.gc(); // 手动触发GC
if(softRef.get() == null){
    
    
    System.out.println("user对象已经被GC回收    " + softRef.get());
}else {
    
    
    System.out.println("user对象没有被GC回收    " + softRef.get());
}
// 模拟内存溢出 这里创建1M的Byte数组
try {
    
    
    Byte[] bytes = new Byte[1024 * 1024 * 1024];
}catch (OutOfMemoryError e){
    
    
    e.printStackTrace();
}
if(softRef.get() == null){
    
    
    System.out.println("user对象已经被GC回收    " + softRef.get());
}else {
    
    
    System.out.println("user对象没有被GC回收    " + softRef.get());
}

软引用可以结合ReferenceQueue来使用,当由于系统内存不足,导致软引用的对象被回收了,JVM会把这个软引用SoftReference对象加入到与之相关联的ReferenceQueue中,SoftReference 对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个 SoftReference 对象的get()方法返回 null,但这个 SoftReference 对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量 SoftReference 对象带来的内存泄漏,使用 ReferenceQueue 清除失去了软引用对象SoftReference。

ReferenceQueue referenceQueue = new ReferenceQueue(); // 引用队列
SoftReference<User> softRef = new SoftReference(new User(),referenceQueue); // 软引用
System.gc(); // 手动触发GC
if(softRef.get() == null){
    
    
    System.out.println("user对象已经被GC回收    " + softRef.get());
}else {
    
    
    System.out.println("user对象没有被GC回收    " + softRef.get());
}
// 模拟内存溢出 这里创建1M的Byte数组
try {
    
    
    Byte[] bytes = new Byte[1024 * 1024 * 1024];
}catch (OutOfMemoryError e){
    
    
    e.printStackTrace();
}
if(softRef.get() == null){
    
    
    System.out.println("user对象已经被GC回收    " + softRef.get());
}else {
    
    
    System.out.println("user对象没有被GC回收    " + softRef.get());
}
// 当软引用User对象被回收后 referenceQueue.poll() 能获取到软引用SoftReference对象
if(softRef == referenceQueue.poll()){
    
    
    softRef = null;
}

弱引用(WeakReference)

只有弱引用的对象,当JVM触发gc时,就会回收该对象。与软引用不同的是,不管是否内存不足,弱引用都会被回收。弱引用也可以结合ReferenceQueue来使用,当由于系统触发gc,导致弱引用的对象被回收了,JVM会把这个弱引用WeakReference对象加入到与之相关联的ReferenceQueue中,不过由于垃圾收集器线程的优先级很低,所以弱引用不一定会被很快回收。

WeakReference<User> weakRef = new WeakReference(new User()); // 弱引用
if(weakRef.get() == null){
    
    
    System.out.println("user对象已经被GC回收    " + weakRef.get());
}else {
    
    
    System.out.println("user对象没有被GC回收    " + weakRef.get());
}
System.gc(); // 手动触发GC
if(weakRef.get() == null){
    
    
    System.out.println("user对象已经被GC回收    " + weakRef.get());
}else {
    
    
    System.out.println("user对象没有被GC回收    " + weakRef.get());
}

虚引用(PhantomReference)

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于,虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。由于Object.finalize()方法的不安全性、低效性,常常使用虚引用完成对象回收前的资源释放工作。

User user = new User(); //创建一个强引用对象
ReferenceQueue referenceQueue = new ReferenceQueue(); // 引用队列
PhantomReference<User> phantomReference = new PhantomReference<>(user, referenceQueue); // 使用虚引用指向这个内存空间 并且绑定引用队列
System.out.println(phantomReference.get()); // 虚引用是获取不到引用值的 get方法返回的null
user = null; // 释放这个内存空间,此时只剩phantomReference通过虚引用指向它
System.gc(); // 手动触发GC
// 被清除的队列中取出被回收的对象
while (true) {
    
    
    Reference<? extends User> poll = referenceQueue.poll();
    if (poll!=null) {
    
    
        System.out.println("虚引用对象被回收 处理其它逻辑");
         return;
    }
}

三、总结

Java的四种引用类型的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用

级别 回收时机 用途
强引用 不会回收 所有的直接引用
软引用 内存不足时回收 联合ReferenceQueue构造有效期短/占内存大/生命周期长的对象的二级高速缓冲器(内存不足才清空)
弱引用 垃圾回收时 联合ReferenceQueue构造有效期短/占内存大/生命周期长的对象的一级高速缓冲器(系统发生gc则清空)
虚引用 垃圾回收时 联合ReferenceQueue来跟踪对象被垃圾回收器回收的活动

猜你喜欢

转载自blog.csdn.net/weixin_44606481/article/details/134808603
今日推荐