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类实现,是一种比强引用弱化但比弱引用强化的引用类型。它的特殊之处在于:只有在内存不足时,被软引用指向的对象才会被回收。
SoftReference<Object> softRef = new SoftReference<>(new Object());软引用非常适合实现内存敏感的缓存。比如图片缓存:
// 创建图片缓存 Map<String, SoftReference<Bitmap>> 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类实现,比软引用的生命周期更短。只要发生垃圾回收,无论内存是否充足,弱引用指向的对象都会被回收。
WeakReference<Object> weakRef = new WeakReference<>(new Object());弱引用的典型应用场景是实现不影响对象生命周期的监听器或观察者模式:
// 弱引用监听器实现 public class WeakListener { private WeakReference<EventListener> 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,它的存在只是为了追踪对象被垃圾回收的状态。
ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);虚引用的主要用途是:
- 精确控制对象回收后的资源释放
- 实现比finalize更可靠的清理机制
典型应用场景是NIO的直接内存管理:
// 虚引用用于直接内存回收 public class DirectBufferCleaner { private static final ReferenceQueue<ByteBuffer> queue = new ReferenceQueue<>(); private static final Set<Reference<?>> 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 PhantomReference<ByteBuffer> { 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中。
ReferenceQueue<Object> queue = new ReferenceQueue<>(); WeakReference<Object> weakRef = new WeakReference<>(new Object(), queue);引用对象在回收过程中会经历四种状态:
- Active:新建时的初始状态
- Pending:即将被放入队列,处于pending-Reference列表
- Enqueued:已被放入队列
- Inactive:已从队列中移除
3.2 引用队列的典型应用
引用队列最常见的用途是实现资源清理:
// 使用引用队列清理资源 public class ResourceCleaner { private final ReferenceQueue<Resource> queue = new ReferenceQueue<>(); private final Set<WeakReference<Resource>> refs = Collections.synchronizedSet(new HashSet<>()); public Resource getResource() { Resource res = new Resource(); WeakReference<Resource> 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中移除。
WeakHashMap<Object, String> map = new WeakHashMap<>(); Object key = new Object(); map.put(key, "value"); // 当key不再有强引用时,Entry会被自动移除 key = null; System.gc(); // 此时map应该是空的WeakHashMap内部通过expungeStaleEntries()方法清理失效的Entry:
private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { Entry<K,V> e = (Entry<K,V>) x; int i = indexFor(e.hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> p = prev; while (p != null) { Entry<K,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 ReferenceQueue<Object> queue = new ReferenceQueue<>(); private final ExecutorService executor = Executors.newSingleThreadExecutor(); public void register(Object obj, Consumer<Object> 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 PhantomReference<Object> { private final Consumer<Object> cleaner; CleanupRef(Object referent, ReferenceQueue<? super Object> q, Consumer<Object> cleaner) { super(referent, q); this.cleaner = cleaner; } void clean() { cleaner.accept(null); } } }6. 引用在JVM中的实现机制
6.1 Reference对象的内部结构
每个Reference对象都包含几个关键字段:
referent:实际引用的对象queue:关联的引用队列next:用于构建引用队列链表discovered:GC发现的引用链pending:待处理的引用列表
JVM通过ReferenceHandler线程处理pending列表:
private static class ReferenceHandler extends Thread { public void run() { while (true) { Reference<Object> 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 Tree
- JProfiler的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 Map<String, WeakReference<Object>> cache = new HashMap<>(); private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); public Object getBean(String name, Supplier<Object> factory) { cleanStaleEntries(); WeakReference<Object> 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 Map<String, SoftReference<Bitmap>> memoryCache = new HashMap<>(); private final ReferenceQueue<Bitmap> queue = new ReferenceQueue<>(); private final Executor cleanupExecutor = Executors.newSingleThreadExecutor(); public Bitmap loadImage(Context context, String url) { cleanStaleReferences(); SoftReference<Bitmap> 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时都会被回收
- 虚引用:无法通过它获取对象,仅用于追踪回收状态
Q2:WeakHashMap的工作原理是什么?
A2:
- 键对象通过弱引用保存
- 当键对象不再有外部强引用时,会被GC回收
- WeakHashMap通过定期清理失效Entry来维护一致性
- 值对象仍然是强引用,需要额外处理才能释放
9.2 实战应用题
Q3:如何实现一个自动清理的资源池?
A3:可以结合弱引用和引用队列实现:
public class AutoCleanupPool { private final Map<Resource, WeakReference<Resource>> pool = new HashMap<>(); private final ReferenceQueue<Resource> 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 Map<OldGenObject, WeakReference<NewGenObject>> bridge = Collections.synchronizedMap(new WeakHashMap<>()); public void link(OldGenObject old, NewGenObject young) { bridge.put(old, new WeakReference<>(young)); } public NewGenObject getYoung(OldGenObject old) { WeakReference<NewGenObject> ref = bridge.get(old); return ref != null ? ref.get() : null; } }10.2 分布式系统中的引用应用
在分布式缓存中,可以结合软引用和一致性哈希实现智能缓存:
public class DistributedCache { private final Map<String, SoftReference<CacheEntry>> localCache = new ConcurrentHashMap<>(); private final ReferenceQueue<CacheEntry> queue = new ReferenceQueue<>(); private final ConsistentHash<Node> hashRing; public Object get(String key) { cleanStaleEntries(); SoftReference<CacheEntry> 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 ReferenceQueue<Resource> queue = new ReferenceQueue<>(); private final Set<ResourceRef> 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 PhantomReference<Resource> { 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 Map<String, WeakReference<Object>> cache = new ConcurrentHashMap<>(); private final ReferenceQueue<Object> 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 ConcurrentMap<String, SoftReference<Object>> cache = new ConcurrentHashMap<>(); private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); private final Lock cleanupLock = new ReentrantLock(); public Object get(String key, Supplier<Object> 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 Map<String, WeakReference<Module>> modules = new HashMap<>(); private final ReferenceQueue<Module> 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) { WeakReference<Module> 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 Map<Class<?>, WeakReference<Object>> services = new HashMap<>(); private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); public <T> void register(Class<T> type, T instance) { cleanStaleServices(); services.put(type, new WeakReference<>(instance, queue)); } @SuppressWarnings("unchecked") public <T> Optional<T> getService(Class<T> type) { cleanStaleServices(); WeakReference<Object> ref = services.get(type); return Optional.ofNullable(ref != null ? (T) ref.get() : null); } }15. 引用与内存泄漏检测
15.1 基于引用的泄漏检测工具
实现简单的内存泄漏检测器:
public class LeakDetector { private final Map<String, WeakReference<Object>> trackedObjects = new HashMap<>(); private final ReferenceQueue<Object> 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 ReferenceQueue<ByteBuffer> queue = new ReferenceQueue<>(); private static final Set<BufferRef> 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 PhantomReference<ByteBuffer> { 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 { ReferenceQueue<Object> queue = new ReferenceQueue<>(); Object testObject = new Object(); WeakReference<Object> 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 { ReferenceQueue<Object> queue = new ReferenceQueue<>(); Map<Integer, SoftReference<byte[]>> 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 (SoftReference<byte[]> 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 ReferenceQueue<ClassLoader> queue = new ReferenceQueue<>(); private final Set<WeakReference<ClassLoader>> 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 Map<String, WeakReference<Class<?>>> classCache = new HashMap<>(); private final ReferenceQueue<Class<?>> 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 Map<Object, WeakReference<Object>> 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; } WeakReference<Object> 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 Map<Integer, WeakReference<Object>> 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(); WeakReference<Object> 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 Map<Object, WeakReference<Object>> cache = new HashMap<>(); @SuppressWarnings("unchecked") public <T, R> Function<T, R> memoize(Function<T, R> function) { return input -> { synchronized (cache) { WeakReference<Object> 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 ReferenceQueue<AutoCloseable> queue = new ReferenceQueue<>(); private final Set<ResourceRef> refs = Collections.synchronizedSet(new HashSet<>()); public <T> Stream<T> track(Stream<T> 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 PhantomReference<AutoCloseable> 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 Map<String, WeakReference<Component>> components = new HashMap<>(); private final ReferenceQueue<Component> queue = new ReferenceQueue<>(); public void