Java中的强引用、软引用、弱引用、虚引用是JDK 1.2之后引入的四种不同强度的引用类型强度从高到低依次为强引用 软引用 弱引用 虚引用核心区别在于垃圾回收器回收对象的时机不同以此实现更灵活的内存管理。一、强引用Strong Reference核心特点是日常代码中最普遍的引用比如Object obj new Object()就是典型的强引用。只要强引用还存在垃圾回收器绝不会回收该对象即使内存不足JVM也宁愿抛出OutOfMemoryError也不会随意回收它。只有将引用显式赋值为null或让引用超出生命周期对象才会被标记为可回收。典型场景日常对象的默认引用比如ArrayList的clear()方法中就通过将数组元素置为null来中断强引用帮助GC及时回收内存。示例代码publicclassStrongReferenceExample{publicstaticvoidmain(String[]args){// 创建强引用ObjectobjnewObject();// 强引用存在时对象不会被回收System.out.println(强引用对象: obj);// 中断强引用objnull;// 此时对象可以被垃圾回收System.gc();System.out.println(强引用已置为null对象可被回收);}}二、软引用Soft Reference核心特点描述有用但非必需的对象。内存空间充足时垃圾回收器不会回收它只有当系统即将发生内存溢出前才会将软引用对象列入回收范围进行二次回收回收后内存仍不足才会抛出OOM。典型场景实现内存敏感的高速缓存比如图片缓存、网页后退页面缓存内存吃紧时自动释放缓存避免OOM内存充足时保留缓存提升访问速度。示例代码importjava.lang.ref.SoftReference;publicclassSoftReferenceExample{publicstaticvoidmain(String[]args){// 创建软引用ObjectobjnewObject();SoftReferenceObjectsoftRefnewSoftReference(obj);// 获取软引用对象ObjectretrievedsoftRef.get();System.out.println(软引用对象: retrieved);// 断开强引用只保留软引用objnull;// 模拟内存不足try{byte[]memoryHognewbyte[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){// 创建弱引用ObjectobjnewObject();WeakReferenceObjectweakRefnewWeakReference(obj);System.out.println(GC前弱引用对象: weakRef.get());// 断开强引用objnull;// 触发垃圾回收System.gc();// 弱引用对象很可能已被回收System.out.println(GC后弱引用对象: weakRef.get());// WeakHashMap示例WeakHashMapObject,StringweakMapnewWeakHashMap();ObjectkeynewObject();weakMap.put(key,value);System.out.println(WeakHashMap大小: weakMap.size());keynull;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{// 创建引用队列ReferenceQueueObjectqueuenewReferenceQueue();// 创建对象和虚引用ObjectobjnewObject();PhantomReferenceObjectphantomRefnewPhantomReference(obj,queue);System.out.println(虚引用get()始终返回null: phantomRef.get());// 断开强引用objnull;// 触发垃圾回收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.idid;}Overrideprotectedvoidfinalize()throwsThrowable{System.out.println([GC] 对象 id 正在被回收...);super.finalize();}OverridepublicStringtoString(){returnMyObject{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(){MyObjectobjnewMyObject(Strong);System.out.println(创建强引用: obj);// 显式调用 GCSystem.gc();try{Thread.sleep(1000);}catch(InterruptedExceptione){}// 结果对象依然存在finalize 不会被调用System.out.println(GC后获取: obj);// 只有断开引用对象才会被回收objnull;System.gc();try{Thread.sleep(1000);}catch(InterruptedExceptione){}System.out.println(断开引用并GC后对象应已被回收。);}/** * 软引用测试 * 特点内存充足时保留内存不足OOM前才回收。 * 注意为了演示效果这里模拟内存压力。 */privatestaticvoidtestSoftReference(){// 创建一个软引用SoftReferenceMyObjectsoftRefnewSoftReference(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(正在分配大量内存以触发软引用回收...);Listbyte[]listnewArrayList();try{// 尝试分配大块内存直到接近堆上限for(inti0;i100;i){list.add(newbyte[1024*1024]);// 每次分配 1MB}}catch(OutOfMemoryErrore){System.out.println(触发 OOMJVM 已尽力回收软引用。);}// 此时软引用对象很可能已经被回收System.out.println(内存压力后获取: softRef.get());}/** * 弱引用测试 * 特点只要发生 GC无论内存是否充足立即回收。 */privatestaticvoidtestWeakReference(){WeakReferenceMyObjectweakRefnewWeakReference(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{ReferenceQueueMyObjectqueuenewReferenceQueue();// 创建虚引用必须传入 ReferenceQueuePhantomReferenceMyObjectphantomRefnewPhantomReference(newMyObject(Phantom),queue);// 虚引用的 get() 永远返回 nullSystem.out.println(虚引用 get(): phantomRef.get());// 启动一个线程监控引用队列ThreadmonitorThreadnewThread(()-{System.out.println([监控线程] 开始监听引用队列...);try{// block 直到队列中有元素Reference?extendsMyObjectrefqueue.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 弱引用想实现缓存希望尽量留住数据实在没内存再删用 软引用。想实现防泄漏希望对象不用了赶紧删不管内存够不够用 弱引用。虚引用必须配队列单独创建虚引用没有任何意义因为它不影响对象生命周期也无法获取对象。