Java引用类型详解:强引用、软引用、弱引用与虚引用

发布时间:2026/7/21 2:23:17
Java引用类型详解:强引用、软引用、弱引用与虚引用 1. Java引用类型深度解析在Java开发中引用这个概念看似简单实则暗藏玄机。记得我刚入行时就因为对引用理解不透彻导致内存泄漏问题排查了整整三天。Java的引用机制直接关系到内存管理和垃圾回收GC的效率是每个Java开发者必须掌握的底层知识。Java引用主要分为四种类型强引用Strong Reference、软引用Soft Reference、弱引用Weak Reference和虚引用Phantom Reference。这四种引用类型构成了Java内存管理的基石它们决定了对象何时可以被垃圾回收以及在不同内存压力下的行为表现。提示理解引用类型的关键在于明白它们与垃圾回收器的交互方式这直接影响到应用的性能和稳定性。2. 四种引用类型详解2.1 强引用默认的引用方式强引用是Java中最常见的引用类型也是默认的引用方式。当你使用new关键字创建一个对象并将其赋值给一个变量时就创建了一个强引用。Object obj new Object(); // 创建强引用强引用的特点是只要强引用存在被引用的对象就永远不会被垃圾回收。即使内存不足时JVM宁愿抛出OutOfMemoryError也不会回收被强引用指向的对象。在实际开发中我们经常需要手动解除强引用obj null; // 显式解除强引用内存泄漏警示集合类中的对象如果不及时清理很容易造成内存泄漏。比如静态Map长期持有对象引用即使这些对象已经不再需要。2.2 软引用内存敏感的缓存方案软引用通过SoftReference类实现是一种比强引用弱化但比弱引用强化的引用类型。它的特殊之处在于只有在内存不足时被软引用指向的对象才会被回收。SoftReferenceObject softRef new SoftReference(new Object());软引用非常适合实现内存敏感的缓存。比如图片缓存// 创建图片缓存 MapString, SoftReferenceBitmap imageCache new HashMap(); // 添加缓存 imageCache.put(logo, new SoftReference(loadBitmap(logo.png))); // 获取缓存 Bitmap logo imageCache.get(logo).get(); if(logo null) { // 缓存被回收重新加载 logo loadBitmap(logo.png); imageCache.put(logo, new SoftReference(logo)); }实战经验Android开发中常用软引用实现图片缓存但要注意在Android 2.3版本后GC行为变得更激进可能需要结合LRU缓存策略。2.3 弱引用不影响GC的观察者模式弱引用通过WeakReference类实现比软引用的生命周期更短。只要发生垃圾回收无论内存是否充足弱引用指向的对象都会被回收。WeakReferenceObject weakRef new WeakReference(new Object());弱引用的典型应用场景是实现不影响对象生命周期的监听器或观察者模式// 弱引用监听器实现 public class WeakListener { private WeakReferenceEventListener listenerRef; public void registerListener(EventListener listener) { this.listenerRef new WeakReference(listener); } public void notifyEvent() { EventListener listener listenerRef.get(); if(listener ! null) { listener.onEvent(); } } }常见误区很多人误以为WeakHashMap的键是弱引用就会自动清理值对象实际上只有键对象会被回收值对象需要通过额外处理才能避免内存泄漏。2.4 虚引用对象回收的精确追踪虚引用是最特殊的一种引用类型通过PhantomReference类实现。与其他引用不同虚引用的get()方法总是返回null它的存在只是为了追踪对象被垃圾回收的状态。ReferenceQueueObject queue new ReferenceQueue(); PhantomReferenceObject phantomRef new PhantomReference(new Object(), queue);虚引用的主要用途是精确控制对象回收后的资源释放实现比finalize更可靠的清理机制典型应用场景是NIO的直接内存管理// 虚引用用于直接内存回收 public class DirectBufferCleaner { private static final ReferenceQueueByteBuffer queue new ReferenceQueue(); private static final SetReference? refs Collections.synchronizedSet(new HashSet()); public static ByteBuffer allocateDirect(int size) { ByteBuffer buffer ByteBuffer.allocateDirect(size); CleanerRef ref new CleanerRef(buffer, queue); refs.add(ref); return buffer; } private static class CleanerRef extends PhantomReferenceByteBuffer { private final long address; CleanerRef(ByteBuffer referent, ReferenceQueue? super ByteBuffer q) { super(referent, q); this.address ((DirectBuffer)referent).address(); } void clean() { // 调用native方法释放直接内存 unsafe.freeMemory(address); refs.remove(this); } } // 清理线程 static { Thread cleanerThread new Thread(() - { while(true) { try { CleanerRef ref (CleanerRef)queue.remove(); ref.clean(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } }); cleanerThread.setDaemon(true); cleanerThread.start(); } }重要提示虚引用必须与ReferenceQueue配合使用否则毫无意义。它提供了比finalize更可靠的资源释放机制。3. ReferenceQueue的工作原理3.1 引用队列的作用机制ReferenceQueue是引用类型的重要搭档它允许我们追踪引用对象的状态变化。当被引用的对象达到合适的回收状态时引用对象本身会被放入关联的ReferenceQueue中。ReferenceQueueObject queue new ReferenceQueue(); WeakReferenceObject weakRef new WeakReference(new Object(), queue);引用对象在回收过程中会经历四种状态Active新建时的初始状态Pending即将被放入队列处于pending-Reference列表Enqueued已被放入队列Inactive已从队列中移除3.2 引用队列的典型应用引用队列最常见的用途是实现资源清理// 使用引用队列清理资源 public class ResourceCleaner { private final ReferenceQueueResource queue new ReferenceQueue(); private final SetWeakReferenceResource refs Collections.synchronizedSet(new HashSet()); public Resource getResource() { Resource res new Resource(); WeakReferenceResource ref new WeakReference(res, queue); refs.add(ref); return res; } public void clean() { Reference? extends Resource ref; while((ref queue.poll()) ! null) { refs.remove(ref); // 执行资源清理操作 } } }性能优化技巧对于高频创建临时对象的场景使用引用队列可以显著降低内存占用但要注意队列处理不能成为性能瓶颈。4. WeakHashMap深度剖析4.1 WeakHashMap的实现原理WeakHashMap是一种特殊的Map实现它的键是弱引用。当键对象不再被外部强引用时对应的Entry会自动从Map中移除。WeakHashMapObject, String map new WeakHashMap(); Object key new Object(); map.put(key, value); // 当key不再有强引用时Entry会被自动移除 key null; System.gc(); // 此时map应该是空的WeakHashMap内部通过expungeStaleEntries()方法清理失效的Entryprivate void expungeStaleEntries() { for (Object x; (x queue.poll()) ! null; ) { synchronized (queue) { EntryK,V e (EntryK,V) x; int i indexFor(e.hash, table.length); EntryK,V prev table[i]; EntryK,V p prev; while (p ! null) { EntryK,V next p.next; if (p e) { if (prev e) table[i] next; else prev.next next; e.value null; // 帮助GC size--; break; } prev p; p next; } } } }4.2 WeakHashMap的使用场景WeakHashMap特别适合以下场景元数据缓存如ClassLoader缓存监听器管理临时对象映射注意事项WeakHashMap的键是弱引用但值仍然是强引用自动清理依赖于垃圾回收时机不确定不是线程安全的多线程环境需要额外同步5. 引用类型的性能考量5.1 不同引用类型的开销对比引用类型虽然强大但使用不当会影响性能引用类型创建开销GC影响适用场景强引用最低可能阻止回收常规对象引用软引用中等内存不足时回收缓存实现弱引用中等下次GC时回收监听器、临时映射虚引用较高需要队列处理资源清理5.2 引用处理的优化策略批量处理引用队列避免频繁检查队列可以定时批量处理控制引用数量大量引用对象会增加GC负担避免引用风暴短时间内创建大量引用对象会导致GC压力// 优化的引用处理方式 public class EfficientRefHandler { private final ReferenceQueueObject queue new ReferenceQueue(); private final ExecutorService executor Executors.newSingleThreadExecutor(); public void register(Object obj, ConsumerObject cleaner) { new CleanupRef(obj, queue, cleaner); executor.submit(this::processQueue); } private void processQueue() { try { while(!Thread.currentThread().isInterrupted()) { CleanupRef ref (CleanupRef)queue.remove(); ref.clean(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } private static class CleanupRef extends PhantomReferenceObject { private final ConsumerObject cleaner; CleanupRef(Object referent, ReferenceQueue? super Object q, ConsumerObject cleaner) { super(referent, q); this.cleaner cleaner; } void clean() { cleaner.accept(null); } } }6. 引用在JVM中的实现机制6.1 Reference对象的内部结构每个Reference对象都包含几个关键字段referent实际引用的对象queue关联的引用队列next用于构建引用队列链表discoveredGC发现的引用链pending待处理的引用列表JVM通过ReferenceHandler线程处理pending列表private static class ReferenceHandler extends Thread { public void run() { while (true) { ReferenceObject r; synchronized (lock) { if (pending ! null) { r pending; pending r.discovered; r.discovered null; } else { // 等待新的pending引用 try { lock.wait(); } catch (InterruptedException x) { break; } continue; } } // 将引用加入队列 ReferenceQueue? super Object q r.queue; if (q ! ReferenceQueue.NULL) q.enqueue(r); } } }6.2 GC与引用的交互过程垃圾回收器处理引用对象的过程标记阶段发现所有可达对象引用处理根据引用类型分类处理清理阶段将适当引用加入pending列表最终处理ReferenceHandler线程处理pending列表性能陷阱大量引用对象会延长GC停顿时间特别是在Full GC时。7. 引用类型的最佳实践7.1 缓存实现方案对比方案优点缺点适用场景强引用性能最好可能内存泄漏小型固定缓存软引用自动释放内存释放时机不确定内存敏感缓存弱引用及时释放缓存命中率低临时缓存LRU缓存可控性强实现复杂大多数缓存场景7.2 内存泄漏排查技巧引用相关内存泄漏的常见表现内存持续增长即使触发GC也不下降软引用缓存从未被回收弱引用对象存活时间过长排查工具VisualVM的Reference跟踪Eclipse MAT的Dominator TreeJProfiler的Reference统计// 诊断引用问题的工具方法 public static void dumpReferenceStats() { Class?[] refClasses { SoftReference.class, WeakReference.class, PhantomReference.class }; for (Class? clazz : refClasses) { try { Field field clazz.getDeclaredField(pending); field.setAccessible(true); Reference? pending (Reference?) field.get(null); int count 0; Reference? current pending; while (current ! null) { count; current current.discovered; } System.out.printf(%s pending count: %d%n, clazz.getSimpleName(), count); } catch (Exception e) { e.printStackTrace(); } } }8. 引用在框架中的应用实例8.1 Spring框架中的引用使用Spring广泛使用弱引用和引用队列来管理Bean生命周期// 类似Spring的Bean缓存实现 public class BeanCache { private final MapString, WeakReferenceObject cache new HashMap(); private final ReferenceQueueObject queue new ReferenceQueue(); public Object getBean(String name, SupplierObject factory) { cleanStaleEntries(); WeakReferenceObject ref cache.get(name); Object bean ref ! null ? ref.get() : null; if (bean null) { bean factory.get(); cache.put(name, new WeakReference(bean, queue)); } return bean; } private void cleanStaleEntries() { Reference? ref; while ((ref queue.poll()) ! null) { cache.values().removeIf(wr - wr ref); } } }8.2 Android中的引用实践Android开发中特别注意内存管理常用引用类型优化性能// Android图片加载优化 public class ImageLoader { private final MapString, SoftReferenceBitmap memoryCache new HashMap(); private final ReferenceQueueBitmap queue new ReferenceQueue(); private final Executor cleanupExecutor Executors.newSingleThreadExecutor(); public Bitmap loadImage(Context context, String url) { cleanStaleReferences(); SoftReferenceBitmap ref memoryCache.get(url); Bitmap bitmap ref ! null ? ref.get() : null; if (bitmap null || bitmap.isRecycled()) { bitmap loadFromDiskOrNetwork(context, url); memoryCache.put(url, new SoftReference(bitmap, queue)); } return bitmap; } private void cleanStaleReferences() { cleanupExecutor.execute(() - { Reference? extends Bitmap ref; while ((ref queue.poll()) ! null) { memoryCache.values().removeIf(r - r ref); } }); } }9. 引用相关的常见面试题解析9.1 基础概念题Q1强引用、软引用、弱引用和虚引用的区别是什么A1强引用默认引用类型只要存在就不会被GC回收软引用内存不足时会被回收适合实现缓存弱引用无论内存是否充足GC时都会被回收虚引用无法通过它获取对象仅用于追踪回收状态Q2WeakHashMap的工作原理是什么A2键对象通过弱引用保存当键对象不再有外部强引用时会被GC回收WeakHashMap通过定期清理失效Entry来维护一致性值对象仍然是强引用需要额外处理才能释放9.2 实战应用题Q3如何实现一个自动清理的资源池A3可以结合弱引用和引用队列实现public class AutoCleanupPool { private final MapResource, WeakReferenceResource pool new HashMap(); private final ReferenceQueueResource queue new ReferenceQueue(); public Resource acquire() { cleanStaleResources(); Resource res findAvailable() ! null ? findAvailable() : createResource(); pool.put(res, new WeakReference(res, queue)); return res; } private void cleanStaleResources() { Reference? extends Resource ref; while ((ref queue.poll()) ! null) { pool.values().removeIf(wr - wr ref); } } }10. 引用类型的高级应用10.1 跨代引用问题解决方案在分代GC中老年代对象引用新生代对象会导致跨代引用问题。JVM使用remembered set和card table来记录这些引用但也可以使用弱引用来优化public class GenerationBridge { private final MapOldGenObject, WeakReferenceNewGenObject bridge Collections.synchronizedMap(new WeakHashMap()); public void link(OldGenObject old, NewGenObject young) { bridge.put(old, new WeakReference(young)); } public NewGenObject getYoung(OldGenObject old) { WeakReferenceNewGenObject ref bridge.get(old); return ref ! null ? ref.get() : null; } }10.2 分布式系统中的引用应用在分布式缓存中可以结合软引用和一致性哈希实现智能缓存public class DistributedCache { private final MapString, SoftReferenceCacheEntry localCache new ConcurrentHashMap(); private final ReferenceQueueCacheEntry queue new ReferenceQueue(); private final ConsistentHashNode hashRing; public Object get(String key) { cleanStaleEntries(); SoftReferenceCacheEntry ref localCache.get(key); CacheEntry entry ref ! null ? ref.get() : null; if (entry null || entry.isExpired()) { Node node hashRing.getNode(key); entry fetchFromNode(node, key); localCache.put(key, new SoftReference(entry, queue)); } return entry.getValue(); } }11. 引用与finalize的对比11.1 finalize方法的局限性finalize方法存在严重问题执行时机不确定可能阻塞GC线程异常会导致对象复活性能开销大11.2 引用替代finalize的方案使用PhantomReference实现更可靠的资源清理public class ResourceHolder { private final ReferenceQueueResource queue new ReferenceQueue(); private final SetResourceRef refs Collections.synchronizedSet(new HashSet()); public Resource acquire() { Resource res new Resource(); refs.add(new ResourceRef(res, queue)); return res; } private static class ResourceRef extends PhantomReferenceResource { private final CleanupTask cleanup; ResourceRef(Resource referent, ReferenceQueue? super Resource q) { super(referent, q); this.cleanup new CleanupTask(referent.getId()); } void clean() { cleanup.execute(); } } // 清理线程 private final Thread cleanerThread new Thread(() - { while (!Thread.currentThread().isInterrupted()) { try { ResourceRef ref (ResourceRef)queue.remove(); ref.clean(); refs.remove(ref); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); }12. 引用在并发环境下的处理12.1 线程安全的引用管理多线程环境下处理引用需要特别注意同步public class ConcurrentRefManager { private final MapString, WeakReferenceObject cache new ConcurrentHashMap(); private final ReferenceQueueObject queue new ReferenceQueue(); private final ScheduledExecutorService cleaner Executors.newSingleThreadScheduledExecutor(); public ConcurrentRefManager() { cleaner.scheduleAtFixedRate(this::cleanStaleEntries, 1, 1, TimeUnit.SECONDS); } public void put(String key, Object value) { cache.put(key, new WeakReference(value, queue)); } private void cleanStaleEntries() { Reference? ref; while ((ref queue.poll()) ! null) { cache.entrySet().removeIf(entry - entry.getValue() ref); } } }12.2 避免引用竞争条件引用处理中的常见竞争条件及解决方案引用对象创建与GC的竞争使用原子引用确保可见性避免在构造函数中泄露this引用队列处理的竞争使用单线程处理队列或使用并发安全队列缓存更新的竞争使用ConcurrentHashMap或使用读写锁保护关键区域public class SafeRefCache { private final ConcurrentMapString, SoftReferenceObject cache new ConcurrentHashMap(); private final ReferenceQueueObject queue new ReferenceQueue(); private final Lock cleanupLock new ReentrantLock(); public Object get(String key, SupplierObject loader) { cleanStaleEntries(); return cache.compute(key, (k, v) - { if (v null || v.get() null) { return new SoftReference(loader.get(), queue); } return v; }).get(); } private void cleanStaleEntries() { if (cleanupLock.tryLock()) { try { Reference? ref; while ((ref queue.poll()) ! null) { cache.values().removeIf(r - r ref); } } finally { cleanupLock.unlock(); } } } }13. 引用与JVM调优13.1 引用相关的JVM参数参数说明默认值建议-XX:SoftRefLRUPolicyMSPerMB每MB堆内存中软引用的存活时间(ms)1000缓存应用可调高-XX:MaxTenuringThreshold对象晋升老年代的年龄阈值15影响弱引用对象生命周期-XX:PrintReferenceGC打印引用处理日志false调试时启用13.2 引用对GC性能的影响引用对象会增加GC的负担引用对象本身需要被标记和清理引用处理需要额外线程工作大量引用会延长GC停顿时间优化建议控制引用对象数量避免在热点代码路径创建引用对于短生命周期对象优先考虑强引用14. 引用在模块化系统中的应用14.1 模块生命周期管理使用弱引用实现模块的热卸载public class ModuleSystem { private final MapString, WeakReferenceModule modules new HashMap(); private final ReferenceQueueModule queue new ReferenceQueue(); public void loadModule(String name, Module module) { cleanUnloadedModules(); modules.put(name, new WeakReference(module, queue)); } private void cleanUnloadedModules() { Reference? extends Module ref; while ((ref queue.poll()) ! null) { modules.values().removeIf(wr - wr ref); } } public void unloadModule(String name) { WeakReferenceModule ref modules.get(name); if (ref ! null) { Module module ref.get(); if (module ! null) { module.cleanup(); } modules.remove(name); } } }14.2 服务发现与引用结合弱引用实现动态服务发现public class ServiceRegistry { private final MapClass?, WeakReferenceObject services new HashMap(); private final ReferenceQueueObject queue new ReferenceQueue(); public T void register(ClassT type, T instance) { cleanStaleServices(); services.put(type, new WeakReference(instance, queue)); } SuppressWarnings(unchecked) public T OptionalT getService(ClassT type) { cleanStaleServices(); WeakReferenceObject ref services.get(type); return Optional.ofNullable(ref ! null ? (T) ref.get() : null); } }15. 引用与内存泄漏检测15.1 基于引用的泄漏检测工具实现简单的内存泄漏检测器public class LeakDetector { private final MapString, WeakReferenceObject trackedObjects new HashMap(); private final ReferenceQueueObject queue new ReferenceQueue(); private final ScheduledExecutorService executor Executors.newSingleThreadScheduledExecutor(); public LeakDetector() { executor.scheduleAtFixedRate(this::checkLeaks, 1, 1, TimeUnit.MINUTES); } public void track(String id, Object obj) { trackedObjects.put(id, new WeakReference(obj, queue)); } private void checkLeaks() { Reference? ref; while ((ref queue.poll()) ! null) { trackedObjects.values().removeIf(wr - wr ref); } trackedObjects.forEach((id, ref) - { if (ref.get() null) { System.err.println(Potential leak detected: id); } }); } }15.2 引用与堆外内存管理使用虚引用监控堆外内存public class DirectMemoryTracker { private static final ReferenceQueueByteBuffer queue new ReferenceQueue(); private static final SetBufferRef refs Collections.synchronizedSet(new HashSet()); public static ByteBuffer allocateDirect(int size) { ByteBuffer buffer ByteBuffer.allocateDirect(size); refs.add(new BufferRef(buffer, queue)); return buffer; } private static class BufferRef extends PhantomReferenceByteBuffer { private final long address; private final int size; BufferRef(ByteBuffer referent, ReferenceQueue? super ByteBuffer q) { super(referent, q); this.address ((DirectBuffer)referent).address(); this.size referent.capacity(); } void clean() { unsafe.freeMemory(address); refs.remove(this); System.out.println(Released direct memory: size bytes); } } static { Thread cleaner new Thread(() - { while (!Thread.currentThread().isInterrupted()) { try { BufferRef ref (BufferRef)queue.remove(); ref.clean(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); cleaner.setDaemon(true); cleaner.start(); } }16. 引用在测试中的应用16.1 内存泄漏测试使用弱引用验证对象是否被正确释放public class MemoryLeakTest { Test public void testNoLeak() throws InterruptedException { ReferenceQueueObject queue new ReferenceQueue(); Object testObject new Object(); WeakReferenceObject ref new WeakReference(testObject, queue); // 解除强引用 testObject null; // 触发GC System.gc(); Thread.sleep(100); // 验证对象已被回收 assertNotNull(queue.poll()); } }16.2 缓存有效性测试验证软引用缓存在内存压力下的行为public class CacheTest { Test public void testSoftCacheUnderMemoryPressure() throws Exception { ReferenceQueueObject queue new ReferenceQueue(); MapInteger, SoftReferencebyte[] cache new HashMap(); // 填充缓存 for (int i 0; i 1000; i) { cache.put(i, new SoftReference(new byte[1024 * 1024], queue)); } // 制造内存压力 try { byte[] memoryHog new byte[1024 * 1024 * 1024]; } catch (OutOfMemoryError e) { // 预期部分缓存会被回收 } // 检查缓存回收情况 int cleared 0; for (SoftReferencebyte[] ref : cache.values()) { if (ref.get() null) { cleared; } } assertTrue(Cache should have cleared some entries, cleared 0); } }17. 引用与类加载器17.1 类加载器泄漏检测使用弱引用检测类加载器泄漏public class ClassLoaderLeakDetector { private final ReferenceQueueClassLoader queue new ReferenceQueue(); private final SetWeakReferenceClassLoader loaders Collections.synchronizedSet(new HashSet()); public void register(ClassLoader loader) { loaders.add(new WeakReference(loader, queue)); } public void checkForLeaks() { Reference? extends ClassLoader ref; while ((ref queue.poll()) ! null) { loaders.remove(ref); } loaders.removeIf(wr - wr.get() null); if (!loaders.isEmpty()) { System.err.println(Potential ClassLoader leaks detected:); loaders.forEach(wr - { ClassLoader loader wr.get(); if (loader ! null) { System.err.println(- loader.getClass().getName()); } }); } } }17.2 动态加载与卸载结合弱引用实现类的热替换public class HotSwapLoader { private final MapString, WeakReferenceClass? classCache new HashMap(); private final ReferenceQueueClass? queue new ReferenceQueue(); public Class? loadClass(String name, byte[] bytecode) { cleanStaleClasses(); Class? clazz defineClass(name, bytecode); classCache.put(name, new WeakReference(clazz, queue)); return clazz; } private void cleanStaleClasses() { Reference? extends Class? ref; while ((ref queue.poll()) ! null) { classCache.values().removeIf(wr - wr ref); } } }18. 引用与序列化18.1 序列化中的引用处理处理序列化中的循环引用public class ReferenceAwareSerializer { private final MapObject, WeakReferenceObject serialized new IdentityHashMap(); public byte[] serialize(Object obj) { ByteArrayOutputStream bos new ByteArrayOutputStream(); try (ObjectOutputStream oos new ObjectOutputStream(bos)) { writeObject(obj, oos); } return bos.toByteArray(); } private void writeObject(Object obj, ObjectOutputStream oos) throws IOException { if (obj null) { oos.writeByte(0); return; } WeakReferenceObject ref serialized.get(obj); if (ref ! null ref.get() ! null) { oos.writeByte(1); // 标记为引用 oos.writeInt(System.identityHashCode(obj)); } else { oos.writeByte(2); // 标记为新对象 serialized.put(obj, new WeakReference(obj)); oos.writeObject(obj); } } }18.2 反序列化中的引用解析public class ReferenceAwareDeserializer { private final MapInteger, WeakReferenceObject deserialized new HashMap(); public Object deserialize(byte[] data) throws Exception { ByteArrayInputStream bis new ByteArrayInputStream(data); try (ObjectInputStream ois new ObjectInputStream(bis)) { return readObject(ois); } } private Object readObject(ObjectInputStream ois) throws Exception { byte marker ois.readByte(); if (marker 0) { return null; } else if (marker 1) { int id ois.readInt(); WeakReferenceObject ref deserialized.get(id); return ref ! null ? ref.get() : null; } else { Object obj ois.readObject(); deserialized.put(System.identityHashCode(obj), new WeakReference(obj)); return obj; } } }19. 引用与函数式编程19.1 惰性求值与引用使用弱引用实现记忆化public class Memoizer { private final MapObject, WeakReferenceObject cache new HashMap(); SuppressWarnings(unchecked) public T, R FunctionT, R memoize(FunctionT, R function) { return input - { synchronized (cache) { WeakReferenceObject ref cache.get(input); R result ref ! null ? (R) ref.get() : null; if (result null) { result function.apply(input); cache.put(input, new WeakReference(result)); } return result; } }; } }19.2 流处理与引用使用虚引用追踪流处理中的资源public class StreamResourceTracker { private final ReferenceQueueAutoCloseable queue new ReferenceQueue(); private final SetResourceRef refs Collections.synchronizedSet(new HashSet()); public T StreamT track(StreamT stream, AutoCloseable resource) { refs.add(new ResourceRef(resource, queue)); return stream.onClose(() - cleanResources()); } private void cleanResources() { Reference? ref; while ((ref queue.poll()) ! null) { refs.remove(ref); } refs.forEach(r - { try { r.close(); } catch (Exception e) { // 记录日志 } }); } private static class ResourceRef extends PhantomReferenceAutoCloseable implements AutoCloseable { private final AutoCloseable resource; ResourceRef(AutoCloseable referent, ReferenceQueue? super AutoCloseable q) { super(referent, q); this.resource referent; } public void close() throws Exception { resource.close(); } } }20. 引用在GUI编程中的应用20.1 Swing/AWT中的引用问题GUI编程中常见的内存泄漏及解决方案public class SafeComponentHolder { private final MapString, WeakReferenceComponent components new HashMap(); private final ReferenceQueueComponent queue new ReferenceQueue(); public void