Java 提供了四种不同强度的引用类型,用于精细控制对象的生命周期和垃圾回收行为。这些引用类型从强到弱分别是:强引用、软引用、弱引用和虚引用。 1. 强引用 (Strong Reference)定义与特点 强引用是 Java 中最常见、最默认的引用类型。只要强引用存在,垃圾回收器就永远不会回收被引用的对象。 关键特性: - 对象不会被垃圾回收,即使内存不足时 JVM 宁愿抛出 OutOfMemoryError
- 引用关系:对象可达 → 不会被回收
- 日常编程中 99% 的使用场景都是强引用
示例代码 public class StrongReferenceExample { public static void main(String[] args) { // 创建强引用 Object obj = new Object(); // 强引用 // 对象可以通过强引用正常访问 System.out.println(obj.toString()); // 显式设置为null,断开强引用 obj = null; // 此时对象变为不可达,可以被垃圾回收 System.gc(); // 建议JVM进行垃圾回收(但不保证立即执行) }}
2. 软引用 (Soft Reference)定义与特点 软引用描述一些"还有用但非必需"的对象。只有在内存不足时,垃圾回收器才会回收软引用指向的对象。 关键特性: - 内存充足时:对象不会被回收
- 内存不足时:对象会被回收(在抛出 OutOfMemoryError 之前)
- 常用于实现内存敏感的缓存
示例代码 import java.lang.ref.SoftReference;public class SoftReferenceExample { public static void main(String[] args) { // 创建一个大对象 byte[] largeObject = new byte[1024 * 1024 * 10]; // 10MB // 创建软引用 SoftReference<byte[]> softRef = new SoftReference<>(largeObject); // 断开强引用,只保留软引用 largeObject = null; // 尝试获取软引用对象 byte[] retrieved = softRef.get(); if (retrieved != null) { System.out.println("对象未被回收,大小: " + retrieved.length + " bytes"); } else { System.out.println("对象已被回收(内存不足)"); } // 模拟内存不足情况(实际应用中通过分配大量内存来触发) try { byte[] memoryHog = new byte[1024 * 1024 * 100]; // 尝试分配100MB } catch (OutOfMemoryError e) { System.out.println("内存不足,软引用可能被回收"); } // 再次检查 if (softRef.get() == null) { System.out.println("确认:软引用对象已被回收"); } }}
3. 弱引用 (Weak Reference)定义与特点 弱引用比软引用的生命周期更短。无论内存是否充足,只要发生垃圾回收,弱引用指向的对象就会被回收。 关键特性: - 下一次垃圾回收时对象就会被回收
- 常用于实现规范化映射(如 WeakHashMap)或监控对象生命周期
- 适合做临时缓存或避免内存泄漏
示例代码 import java.lang.ref.WeakReference;public class WeakReferenceExample { public static void main(String[] args) { // 创建对象 String data = new String("重要数据"); // 创建弱引用 WeakReference<String> weakRef = new WeakReference<>(data); System.out.println("GC前: " + weakRef.get()); // 断开强引用 data = null; // 强制垃圾回收 System.gc(); // 给GC一点时间 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // 检查对象是否被回收 if (weakRef.get() == null) { System.out.println("对象已被垃圾回收"); } else { System.out.println("对象仍然存在: " + weakRef.get()); } }}
4. 虚引用 (Phantom Reference)定义与特点 虚引用是最弱的一种引用关系。无法通过虚引用来获取对象实例,主要用于跟踪对象被垃圾回收的状态。 关键特性: - 无法通过 get() 方法获取对象(总是返回 null)
- 必须与 ReferenceQueue 一起使用
- 对象被回收时,虚引用会被加入到引用队列
- 用于在对象被回收时执行清理操作
示例代码 import java.lang.ref.PhantomReference;import java.lang.ref.ReferenceQueue;public class PhantomReferenceExample { public static void main(String[] args) { // 创建引用队列 ReferenceQueue<Object> queue = new ReferenceQueue<>(); // 创建对象 Object resource = new Object(); System.out.println("原始对象: " + resource); // 创建虚引用 PhantomReference<Object> phantomRef = new PhantomReference<>(resource, queue); // 虚引用的get()方法总是返回null System.out.println("虚引用get(): " + phantomRef.get()); // 输出null // 断开强引用 resource = null; // 启动监控线程 Thread monitorThread = new Thread(() -> { try { // 阻塞等待,直到有引用进入队列 PhantomReference<?> ref = (PhantomReference<?>) queue.remove(); System.out.println("检测到对象已被回收,可以执行清理操作"); } catch (InterruptedException e) { e.printStackTrace(); } }); monitorThread.start(); // 强制垃圾回收 System.gc(); try { monitorThread.join(1000); } catch (InterruptedException e) { e.printStackTrace(); } }}
引用队列 (ReferenceQueue)引用队列与软引用、弱引用、虚引用配合使用,当引用的对象被垃圾回收后,引用本身会被加入到引用队列中。 综合示例 import java.lang.ref.*;public class ReferenceQueueExample { public static void main(String[] args) { ReferenceQueue<Object> queue = new ReferenceQueue<>(); // 创建不同引用的对象 Object strongObj = new Object(); SoftReference<Object> softRef = new SoftReference<>(new Object(), queue); WeakReference<Object> weakRef = new WeakReference<>(new Object(), queue); PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue); System.out.println("初始状态:"); System.out.println("强引用: " + strongObj); System.out.println("软引用: " + softRef.get()); System.out.println("弱引用: " + weakRef.get()); System.out.println("虚引用: " + phantomRef.get()); // 断开强引用,保留各种引用 strongObj = null; // 强制垃圾回收 System.gc(); // 监控引用队列 new Thread(() -> { try { int count = 0; while (count < 3) { // 预期会收到3个引用 Reference<?> ref = queue.remove(1000); // 等待1秒 if (ref != null) { count++; if (ref instanceof SoftReference) { System.out.println("软引用进入队列"); } else if (ref instanceof WeakReference) { System.out.println("弱引用进入队列"); } else if (ref instanceof PhantomReference) { System.out.println("虚引用进入队列"); } } } } catch (InterruptedException e) { e.printStackTrace(); } }).start(); }}
实际应用场景1. 缓存实现(软引用) import java.lang.ref.SoftReference;import java.util.concurrent.ConcurrentHashMap;public class ImageCache { private final ConcurrentHashMap<String, SoftReference<byte[]>> cache = new ConcurrentHashMap<>(); public void putImage(String key, byte[] imageData) { cache.put(key, new SoftReference<>(imageData)); } public byte[] getImage(String key) { SoftReference<byte[]> ref = cache.get(key); if (ref != null) { byte[] data = ref.get(); if (data != null) { return data; // 缓存命中 } else { cache.remove(key); // 清理已回收的引用 } } return null; // 缓存未命中 } public void clear() { cache.clear(); }}
2. 防止内存泄漏(弱引用 + WeakHashMap) import java.util.Map;import java.util.WeakHashMap;public class ObjectRegistry { private final Map<Object, String> registry = new WeakHashMap<>(); public void register(Object obj, String name) { registry.put(obj, name); } public String getName(Object obj) { return registry.get(obj); } // 当对象没有被其他强引用时,会自动从WeakHashMap中移除}
3. 资源清理(虚引用) import java.lang.ref.PhantomReference;import java.lang.ref.ReferenceQueue;public class ResourceManager { private static class CleanupReference extends PhantomReference<Object> { private final Runnable cleanupTask; public CleanupReference(Object resource, ReferenceQueue<? super Object> queue, Runnable cleanupTask) { super(resource, queue); this.cleanupTask = cleanupTask; } public void cleanup() { if (cleanupTask != null) { cleanupTask.run(); } } } private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); private final Map<CleanupReference, Object> references = new java.util.HashMap<>(); public void registerResource(Object resource, Runnable cleanup) { CleanupReference ref = new CleanupReference(resource, queue, cleanup); references.put(ref, null); // 启动清理线程 startCleanupThread(); } private void startCleanupThread() { Thread cleanupThread = new Thread(() -> { while (true) { try { CleanupReference ref = (CleanupReference) queue.remove(); ref.cleanup(); references.remove(ref); } catch (InterruptedException e) { break; } } }); cleanupThread.setDaemon(true); cleanupThread.start(); }}
总结对比表特性 | 强引用 | 软引用 | 弱引用 | 虚引用 | 回收时机 | 永不回收 | 内存不足时 | 下次GC时 | 不确定,用于跟踪 | get()返回值 | 对象本身 | 对象(未回收时) | 对象(未回收时) | 总是null | 引用队列 | 不支持 | 可选 | 可选 | 必须使用 | 主要用途 | 普通对象引用 | 内存敏感缓存 | 临时缓存、防泄漏 | 资源清理跟踪 | 内存敏感度 | 不敏感 | 高敏感 | 非常敏感 | 无关 |
最佳实践与注意事项1. 合理选择引用类型 // 根据需求选择合适的引用类型public class ReferenceChoice { // 长期存在的核心数据 - 使用强引用 private final Map<String, User> userCache = new HashMap<>(); // 图片等大对象缓存 - 使用软引用 private final Map<String, SoftReference<byte[]>> imageCache = new HashMap<>(); // 临时性的监听器 - 使用弱引用防止内存泄漏 private final Map<EventListener, WeakReference<EventListener>> listeners = new HashMap<>();}
2. 避免常见陷阱 public class ReferencePitfalls { public void commonMistakes() { // 错误:弱引用被强引用持有,失去意义 String data = new String("data"); WeakReference<String> weakRef = new WeakReference<>(data); // data 强引用仍然存在,weakRef 指向的对象不会被回收 // 正确:及时断开强引用 WeakReference<String> correctRef = new WeakReference<>(new String("data")); // 或者 String temp = new String("data"); WeakReference<String> weakRef2 = new WeakReference<>(temp); temp = null; // 及时断开强引用 }}
要是能把这些引用类型用对了,就能帮咱们编出更高效、更稳定的 Java 应用程序。尤其是在那些得精准控制内存使用的情况里,这方法特别管用。 查看详情:https://www.toutiao.com/article/7582071566893122111 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |