1. 强引用(Strong Reference)
基本概念
强引用是Java中最常见的引用类型,也是默认的引用方式
。
// 强引用示例
public class StrongReferenceExample {
public static void main(String[] args) {
Object strongRef = new Object(); // 强引用
// 手动解除引用
strongRef = null; // 现在可以被GC回收
}
}
特点
- 只要强引用存在,垃圾回收器就永远不会回收被引用的对象
- 即使内存不足时,JVM宁愿抛出OutOfMemoryError也不会回收强引用对象
使用场景
- 日常开发中绝大多数对象的引用
- 需要长期存在的对象
注意事项
- 注意避免
内存泄漏
:当不再需要对象时,应及时将引用置为null
- 集合类中的对象要注意及时清理
2. 软引用(Soft Reference)
软引用(SoftReference) 是一种比弱引用更“温和”的引用类型,它会在内存不足时(OOM前) 被垃圾回收器(GC)回收,而不是像弱引用那样立即回收。软引用非常适合用于实现内存敏感的缓存(例如图片缓存、临时数据缓存等)。
基本概念
软引用通过java.lang.ref.SoftReference
类实现。
import java.lang.ref.SoftReference;
public class SoftReferenceExample {
public static void main(String[] args) {
SoftReference<byte[]> softRef = new SoftReference<>(new byte[1024 * 1024]); // 1MB缓存
// 使用前检查
byte[] cache = softRef.get();
if (cache != null) {
System.out.println("从缓存读取数据");
} else {
System.out.println("缓存已被回收,重新创建");
cache = new byte[1024 * 1024];
softRef = new SoftReference<>(cache);
}
}
}
特点
- 当内存不足时,垃圾回收器会回收软引用指向的对象
- 在内存充足时,软引用对象不会被回收
使用场景
- 实现
内存敏感
的缓存(如配置、资源缓存) - 适合缓存那些可以重建但
重建成本较高
的对象
注意事项
- 不要过度依赖软引用作为唯一的数据存储方式
- 获取对象时需要检查是否为null(可能已被回收)
- 在Android开发中,软引用的行为可能有差异
结合 ReferenceQueue 监控回收
软引用可以和 ReferenceQueue 结合使用,以便在对象被回收时得到通知。
import java.lang.ref.SoftReference
import java.lang.ref.ReferenceQueue
fun main() {
val referenceQueue = ReferenceQueue<Hero>()
// 创建软引用并关联到队列
val softHero = SoftReference(Hero("Lancelot", 200), referenceQueue)
// 模拟内存不足(分配大量对象)
val list = mutableListOf<ByteArray>()
repeat(100) {
list.add(ByteArray(1024 * 1024)) // 每次分配1MB
println("Memory pressure... SoftRef: ${softHero.get() ?: "NULL"}")
Thread.sleep(50)
}
// 检查队列是否有被回收的软引用
val recycledRef = referenceQueue.poll() as? SoftReference<Hero>
if (recycledRef != null) {
println("软引用已被回收") // 输出: 软引用已被回收
}
}
3. 弱引用(Weak Reference)
基本概念
弱引用通过java.lang.ref.WeakReference
类实现。
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
public class WeakReferenceExample {
public static void main(String[] args) {
// 弱引用示例
WeakReference<Object> weakRef = new WeakReference<>(new Object());
// WeakHashMap使用示例
WeakHashMap<Object, String> weakMap = new WeakHashMap<>();
Object key = new Object();
weakMap.put(key, "临时数据");
key = null; // 移除强引用
System.gc(); // 触发GC后weakMap会自动清理
}
}
特点
- 无论内存是否充足,只要发生GC,
弱引用
对象就会被回收 - 比软引用具有更短的生命周期
使用场景
- 实现不会阻止对象被回收的监听器/观察者模式
- 用于构建规范映射(如WeakHashMap)
- 适用于临时性的、可随时重建的对象引用
注意事项
- 获取对象前必须检查是否为null
- 不适合用于必须长期存在的对象
4. 虚引用(Phantom Reference)
基本概念
虚引用通过java.lang.ref.PhantomReference
类实现,必须与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();
PhantomReference<Object> phantomRef = new PhantomReference<>(resource, queue);
resource = null; // 释放强引用
// 监控回收队列的线程
new Thread(() -> {
try {
System.out.println("等待虚引用被回收...");
queue.remove(); // 阻塞直到有引用入队
System.out.println("检测到资源被回收,执行清理操作");
// 这里可以执行native资源释放等操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
System.gc(); // 触发GC
}
}
特点
- 最弱的引用类型,无法通过虚引用获取对象实例
- 对象被回收时,虚引用会被放入关联的
ReferenceQueue
- 主要用于跟踪对象被垃圾回收的活动
使用场景
- 精细化的资源清理(如
native
资源释放) - 对象生命周期跟踪和监控
- 实现比
finalize
更可靠的清理机制
注意事项
- 不能用于常规的对象引用
- 需要配合
ReferenceQueue
使用 - 主要用于特殊的内存管理场景
引用类型对比
引用类型 | 回收时机 | 是否可通过get()获取 | 典型用途 |
---|---|---|---|
强引用 | 永不自动回收 | 是 | 常规对象引用 |
软引用 | 内存不足时 | 是 | 内存敏感缓存 |
弱引用 | GC运行时 | 是 | 临时映射/监听器 |
虚引用 | GC运行时 | 否 | 资源回收跟踪 |
实际应用建议
缓存实现
// 基于软引用的缓存实现
public class SoftCache<K, V> {
private final Map<K, SoftReference<V>> cache = new HashMap<>();
public void put(K key, V value) {
cache.put(key, new SoftReference<>(value));
}
public V get(K key) {
SoftReference<V> ref = cache.get(key);
return ref != null ? ref.get() : null;
}
}
资源清理模式
// 使用虚引用管理native资源
public class NativeResourceHolder {
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
private final Set<PhantomReference<Object>> refs = Collections.newSetFromMap(new WeakHashMap<>());
public void registerResource(Object resource, Runnable cleanupTask) {
refs.add(new PhantomReference<>(resource, queue) {
@Override
public void clear() {
cleanupTask.run();
super.clear();
}
});
}
}
内存泄漏检测
// 弱引用+ReferenceQueue检测泄漏
public class LeakDetector {
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
private final Map<String, WeakReference<Object>> refs = new HashMap<>();
public void register(Object obj, String id) {
refs.put(id, new WeakReference<>(obj, queue));
}
public void checkLeaks() {
Reference<?> ref;
while ((ref = queue.poll()) != null) {
// 找出泄漏的key
refs.entrySet().removeIf(entry -> entry.getValue() == ref);
}
}
}
总结
- 使用软引用缓存玩家常用数据
- 使用弱引用管理事件监听器
- 避免在核心服务中使用弱/软引用
- 内存敏感场景