Java中的强引用、软引用、弱引用、虚引用

Java中的强引用、软引用、弱引用、虚引用是JDK 1.2之后引入的四种不同强度的引用类型,强度从高到低依次为:‌强引用 > 软引用 > 弱引用 > 虚引用‌,核心区别在于垃圾回收器回收对象的时机不同,以此实现更灵活的内存管理。

一、强引用(Strong Reference)

‌核心特点‌:是日常代码中最普遍的引用,比如Object obj = new Object()就是典型的强引用。只要强引用还存在,垃圾回收器绝不会回收该对象,即使内存不足,JVM也宁愿抛出OutOfMemoryError也不会随意回收它。只有将引用显式赋值为null,或让引用超出生命周期,对象才会被标记为可回收。
‌典型场景‌:日常对象的默认引用,比如ArrayList的clear()方法中,就通过将数组元素置为null来中断强引用,帮助GC及时回收内存。

示例代码:

publicclassStrongReferenceExample{publicstaticvoidmain(String[]args){// 创建强引用Objectobj=newObject();// 强引用存在时,对象不会被回收System.out.println("强引用对象: "+obj);// 中断强引用obj=null;// 此时对象可以被垃圾回收System.gc();System.out.println("强引用已置为null,对象可被回收");}}

二、软引用(Soft Reference)

‌核心特点‌:描述有用但非必需的对象。内存空间充足时,垃圾回收器不会回收它;只有当系统即将发生内存溢出前,才会将软引用对象列入回收范围进行二次回收,回收后内存仍不足才会抛出OOM。
‌典型场景‌:实现内存敏感的高速缓存,比如图片缓存、网页后退页面缓存,内存吃紧时自动释放缓存避免OOM,内存充足时保留缓存提升访问速度。

示例代码:

importjava.lang.ref.SoftReference;publicclassSoftReferenceExample{publicstaticvoidmain(String[]args){// 创建软引用Objectobj=newObject();SoftReference<Object>softRef=newSoftReference<>(obj);// 获取软引用对象Objectretrieved=softRef.get();System.out.println("软引用对象: "+retrieved);// 断开强引用,只保留软引用obj=null;// 模拟内存不足try{byte[]memoryHog=newbyte[1024*1024*100];// 申请大量内存}catch(OutOfMemoryErrore){System.out.println("内存不足,软引用可能被回收");}// 检查软引用是否被回收System.out.println("软引用对象是否被回收: "+(softRef.get()==null));}}

三、弱引用(Weak Reference)

‌核心特点‌:强度比软引用更弱,只要发生垃圾回收,无论当前内存是否足够,都会回收被弱引用关联的对象,对象只能生存到下一次垃圾收集发生之前。
‌典型场景‌:解决回调监听、静态内部类等场景下的内存泄漏问题,也可用于构建自动释放的轻量缓存,比如WeakHashMap就基于弱引用实现。

示例代码:

importjava.lang.ref.WeakReference;publicclassWeakReferenceExample{publicstaticvoidmain(String[]args){// 创建弱引用Objectobj=newObject();WeakReference<Object>weakRef=newWeakReference<>(obj);System.out.println("GC前弱引用对象: "+weakRef.get());// 断开强引用obj=null;// 触发垃圾回收System.gc();// 弱引用对象很可能已被回收System.out.println("GC后弱引用对象: "+weakRef.get());// WeakHashMap示例WeakHashMap<Object,String>weakMap=newWeakHashMap<>();Objectkey=newObject();weakMap.put(key,"value");System.out.println("WeakHashMap大小: "+weakMap.size());key=null;System.gc();System.out.println("GC后WeakHashMap大小: "+weakMap.size());}}

四、虚引用(Phantom Reference)

‌核心特点‌:是最弱的引用关系,完全不影响对象的生存时间,也无法通过虚引用获取对象实例。它必须和引用队列联合使用,当对象被GC回收前,虚引用会被加入关联的引用队列中。
‌典型场景‌:跟踪对象被垃圾回收的活动,实现精细化的后续清理工作,比如JDK NIO中的Cleaner类就用虚引用管理堆外内存,在对象回收后释放对应的非Java堆资源。
核心区别汇总

示例代码:

importjava.lang.ref.PhantomReference;importjava.lang.ref.ReferenceQueue;publicclassPhantomReferenceExample{publicstaticvoidmain(String[]args)throwsInterruptedException{// 创建引用队列ReferenceQueue<Object>queue=newReferenceQueue<>();// 创建对象和虚引用Objectobj=newObject();PhantomReference<Object>phantomRef=newPhantomReference<>(obj,queue);System.out.println("虚引用get()始终返回null: "+phantomRef.get());// 断开强引用obj=null;// 触发垃圾回收System.gc();Thread.sleep(100);// 给GC一点时间// 检查引用队列PhantomReference<?>ref=(PhantomReference<?>)queue.poll();if(ref!=null){System.out.println("对象已被回收,虚引用进入队列");// 执行清理操作System.out.println("执行后续清理工作...");}}}
引用类型回收触发时机是否能获取对象是否必须关联引用队列
强引用引用中断后才可能回收
软引用内存不足时回收可选
弱引用发生GC就回收可选
虚引用GC回收前入队

五,Java 示例代码

importjava.lang.ref.*;importjava.util.ArrayList;importjava.util.List;publicclassReferenceDemo{// 辅助类:用于观察对象是否被回收(finalize方法在GC回收前调用)staticclassMyObject{privateStringid;publicMyObject(Stringid){this.id=id;}@Overrideprotectedvoidfinalize()throwsThrowable{System.out.println("[GC] 对象 "+id+" 正在被回收...");super.finalize();}@OverridepublicStringtoString(){return"MyObject{"+id+"}";}}publicstaticvoidmain(String[]args)throwsInterruptedException{System.out.println("=== 1. 强引用 (Strong Reference) ===");testStrongReference();System.out.println("\n=== 2. 软引用 (Soft Reference) ===");testSoftReference();System.out.println("\n=== 3. 弱引用 (Weak Reference) ===");testWeakReference();System.out.println("\n=== 4. 虚引用 (Phantom Reference) ===");testPhantomReference();}/** * 强引用测试 * 特点:只要引用存在,GC 不会回收。 */privatestaticvoidtestStrongReference(){MyObjectobj=newMyObject("Strong");System.out.println("创建强引用: "+obj);// 显式调用 GCSystem.gc();try{Thread.sleep(1000);}catch(InterruptedExceptione){}// 结果:对象依然存在,finalize 不会被调用System.out.println("GC后获取: "+obj);// 只有断开引用,对象才会被回收obj=null;System.gc();try{Thread.sleep(1000);}catch(InterruptedExceptione){}System.out.println("断开引用并GC后,对象应已被回收。");}/** * 软引用测试 * 特点:内存充足时保留,内存不足(OOM前)才回收。 * 注意:为了演示效果,这里模拟内存压力。 */privatestaticvoidtestSoftReference(){// 创建一个软引用SoftReference<MyObject>softRef=newSoftReference<>(newMyObject("Soft"));System.out.println("创建软引用: "+softRef.get());// 1. 内存充足时,GC 不会回收软引用System.gc();try{Thread.sleep(1000);}catch(InterruptedExceptione){}System.out.println("内存充足,GC后获取: "+softRef.get());// 2. 模拟内存不足:分配大量内存迫使 JVM 回收软引用// 注意:实际生产中,JVM 参数 -Xmx 设置较小时更容易触发System.out.println("正在分配大量内存以触发软引用回收...");List<byte[]>list=newArrayList<>();try{// 尝试分配大块内存,直到接近堆上限for(inti=0;i<100;i++){list.add(newbyte[1024*1024]);// 每次分配 1MB}}catch(OutOfMemoryErrore){System.out.println("触发 OOM,JVM 已尽力回收软引用。");}// 此时软引用对象很可能已经被回收System.out.println("内存压力后获取: "+softRef.get());}/** * 弱引用测试 * 特点:只要发生 GC,无论内存是否充足,立即回收。 */privatestaticvoidtestWeakReference(){WeakReference<MyObject>weakRef=newWeakReference<>(newMyObject("Weak"));System.out.println("创建弱引用: "+weakRef.get());// 显式调用 GCSystem.gc();try{Thread.sleep(1000);}catch(InterruptedExceptione){}// 结果:对象必定被回收,get() 返回 nullSystem.out.println("GC后获取: "+weakRef.get());}/** * 虚引用测试 * 特点:无法通过 get() 获取对象,必须配合 ReferenceQueue 监控回收事件。 */privatestaticvoidtestPhantomReference()throwsInterruptedException{ReferenceQueue<MyObject>queue=newReferenceQueue<>();// 创建虚引用,必须传入 ReferenceQueuePhantomReference<MyObject>phantomRef=newPhantomReference<>(newMyObject("Phantom"),queue);// 虚引用的 get() 永远返回 nullSystem.out.println("虚引用 get(): "+phantomRef.get());// 启动一个线程监控引用队列ThreadmonitorThread=newThread(()->{System.out.println("[监控线程] 开始监听引用队列...");try{// block 直到队列中有元素Reference<?extendsMyObject>ref=queue.remove();System.out.println("[监控线程] 收到通知:对象已被 GC 回收!引用对象: "+ref);}catch(InterruptedExceptione){e.printStackTrace();}});monitorThread.start();// 让主线程休眠一下,确保监控线程启动Thread.sleep(500);// 触发 GCSystem.gc();// 等待监控线程处理monitorThread.join();}}

1,示例代码运行关键点解析

‌强引用‌:

代码中 obj = null 之前,即使调用 System.gc(),对象也不会被回收。
这是最常见的引用,如 List list = new ArrayList<>();。
‌软引用‌:

在内存充足时,softRef.get() 能拿到对象。
当代码中通过 list.add(new byte[…]) 耗尽堆内存时,JVM 会在抛出 OutOfMemoryError 之前清理软引用。
‌应用场景‌:图片缓存、数据库连接池缓存。如果内存够就用缓存,不够就释放,避免 OOM。
‌弱引用‌:

一旦执行 System.gc(),weakRef.get() 立即变为 null。
‌应用场景‌:WeakHashMap、ThreadLocal 的 Key。防止因为长生命周期对象持有短生命周期对象的引用而导致内存泄漏。
‌虚引用‌:

phantomRef.get() 始终为 null,你无法通过它访问对象。
它的核心价值在于 ‌ReferenceQueue‌。当对象被 GC 回收时,JVM 会将这个虚引用对象放入队列中。
‌应用场景‌:替代 finalize() 方法进行更安全的资源清理(如 NIO 中的 DirectByteBuffer 清理堆外内存)。

2. 常见面试/开发注意事项

‌System.gc() 不保证立即执行‌:在实际生产环境中,GC 是由 JVM 自动管理的,System.gc() 只是建议 JVM 进行垃圾回收,不一定立刻生效。但在测试引用行为时,通常配合 Thread.sleep() 来等待 GC 线程完成工作。
‌软引用 vs 弱引用‌:
想实现‌缓存‌(希望尽量留住数据,实在没内存再删)用 ‌软引用‌。
想实现‌防泄漏‌(希望对象不用了赶紧删,不管内存够不够)用 ‌弱引用‌。
‌虚引用必须配队列‌:单独创建虚引用没有任何意义,因为它不影响对象生命周期,也无法获取对象。