1. 强引用(Strong Reference)
内部机制
- 定义: 强引用是最基础的对象引用类型。只要对象存在至少一个强引用指向它,垃圾回收器就不会回收该对象。
- 生命周期管理: 对象的生命周期由强引用控制。只有当所有强引用都被解除(如将引用设为
null
),对象才可能被垃圾回收。 - 特点:
- 是最常用的引用类型。
- 不会被垃圾回收器回收,除非程序显式地解除引用。
编程示例
public class StrongReferenceExample {
public static void main(String[] args) throws InterruptedException {
MyObject obj = new MyObject(); // 创建一个强引用
System.out.println("Before nullifying: " + obj);
obj = null; // 解除强引用
System.gc(); // 建议JVM进行垃圾回收
Thread.sleep(100); // 等待垃圾回收完成
// 尝试访问已被解除引用的对象
if (obj == null) {
System.out.println("Object has been garbage collected.");
}
}
}
class MyObject {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObject is being finalized.");
}
}
应用场景
- 几乎所有的对象创建都是通过强引用实现的,适用于需要长期保持对象存活的场景。
2. 软引用(Soft Reference)
内部机制
- 定义: 软引用用于描述有用但非必需的对象。当内存不足时,垃圾收集器会尝试回收这些对象以释放更多的内存空间。
- 生命周期管理: 在内存充足的情况下,软引用不会被回收;当系统即将发生内存溢出异常之前,垃圾收集器会回收这些对象。
- 特点:
- 可以用来构建缓存系统,确保在内存紧张时自动释放缓存数据。
- 使用
java.lang.ref.SoftReference
类实现。
编程示例
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class SoftReferenceCacheExample {
private final ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
private final Map<String, SoftReference<MyObject>> cache = new HashMap<>();
public void put(String key, MyObject value) {
cache.put(key, new SoftReference<>(value, queue));
}
public MyObject get(String key) {
SoftReference<MyObject> ref = cache.get(key);
return ref != null ? ref.get() : null;
}
public void cleanUp() {
Reference<? extends MyObject> ref;
while ((ref = queue.poll()) != null) {
String keyToRemove = null;
for (Map.Entry<String, SoftReference<MyObject>> entry : cache.entrySet()) {
if (entry.getValue() == ref) {
keyToRemove = entry.getKey();
break;
}
}
if (keyToRemove != null) {
cache.remove(keyToRemove);
System.out.println("Cleaned up: " + keyToRemove);
}
}
}
public static void main(String[] args) throws InterruptedException {
SoftReferenceCacheExample example = new SoftReferenceCacheExample();
example.put("key1", new MyObject());
MyObject obj = example.get("key1");
if (obj == null) {
System.out.println("Cache was cleared due to memory pressure.");
} else {
System.out.println("Retrieved from cache: " + obj);
}
// 模拟内存压力
for (int i = 0; i < 1000000; i++) {
byte[] largeArray = new byte[1024 * 1024]; // 分配大数组
}
example.cleanUp();
obj = example.get("key1");
if (obj == null) {
System.out.println("Cache was cleared due to memory pressure after GC.");
} else {
System.out.println("Retrieved from cache after GC: " + obj);
}
}
}
class MyObject {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObject is being finalized.");
}
}
应用场景
- 主要应用于缓存机制,特别是那些可以重新生成的数据。例如,图片缓存、页面缓存等。
3. 弱引用(Weak Reference)
内部机制
- 定义: 弱引用比软引用更脆弱,一旦发生垃圾回收,不管当前内存是否充足,所有只被弱引用关联的对象都会被回收。
- 生命周期管理: 只要有一次垃圾回收操作,所有仅被弱引用指向的对象都将被回收。
- 特点:
- 适合用于规范化映射(canonicalized mapping),如
WeakHashMap
。 - 使用
java.lang.ref.WeakReference
类实现。
- 适合用于规范化映射(canonicalized mapping),如
编程示例
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class WeakReferenceExample {
private final Map<String, WeakReference<MyObject>> map = new HashMap<>();
private final ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
public void addObject(String key, MyObject obj) {
map.put(key, new WeakReference<>(obj, queue));
}
public MyObject getObject(String key) {
WeakReference<MyObject> ref = map.get(key);
return ref != null ? ref.get() : null;
}
public void cleanUp() {
Reference<? extends MyObject> ref;
while ((ref = queue.poll()) != null) {
String keyToRemove = null;
for (Map.Entry<String, WeakReference<MyObject>> entry : map.entrySet()) {
if (entry.getValue() == ref) {
keyToRemove = entry.getKey();
break;
}
}
if (keyToRemove != null) {
map.remove(keyToRemove);
System.out.println("Cleaned up: " + keyToRemove);
}
}
}
public static void main(String[] args) throws InterruptedException {
WeakReferenceExample example = new WeakReferenceExample();
MyObject obj = new MyObject();
example.addObject("key", obj);
MyObject retrievedObj = example.getObject("key");
if (retrievedObj == null) {
System.out.println("Object was garbage collected.");
} else {
System.out.println("Retrieved object: " + retrievedObj);
}
// 模拟垃圾回收
obj = null;
System.gc();
Thread.sleep(100); // 等待垃圾回收完成
example.cleanUp();
retrievedObj = example.getObject("key");
if (retrievedObj == null) {
System.out.println("Object was garbage collected after GC.");
} else {
System.out.println("Retrieved object after GC: " + retrievedObj);
}
}
}
class MyObject {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObject is being finalized.");
}
}
应用场景
- 常见于规范化映射(canonicalized mapping),如
WeakHashMap
,它可以自动清除不再使用的映射项。也适用于监听器或回调函数的管理。
4. 虚引用(Phantom Reference)
内部机制
- 定义: 虚引用是最弱的一种引用关系,它的主要目的是在对象被垃圾回收前得到通知。虚引用不会影响对象的生命周期,也不能通过虚引用来获取对象实例。
- 生命周期管理: 必须配合
ReferenceQueue
一起使用,以便在对象被回收时收到通知。 - 特点:
- 主要用于在对象被回收前执行一些特定的操作,比如清理资源、记录日志等。
- 使用
java.lang.ref.PhantomReference
类实现。
编程示例
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class PhantomReferenceExample {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
MyObject obj = new MyObject();
PhantomReference<MyObject> phantomRef = new PhantomReference<>(obj, queue);
obj = null; // 解除强引用
System.gc(); // 建议JVM进行垃圾回收
Thread.sleep(100); // 等待垃圾回收完成
PhantomReference<?> ref;
while ((ref = (PhantomReference<?>) queue.poll()) != null) {
System.out.println("Object was garbage collected and notified via PhantomReference.");
}
}
}
class MyObject {
@Override
protected void finalize() throws Throwable {
System.out.println("MyObject is being finalized.");
}
}
应用场景
- 主要用于在对象被回收前执行一些特定的操作,比如清理资源、记录日志等。虚引用通常不用于日常开发中的对象引用管理。
高级主题与最佳实践
缓存机制的最佳实践
- 使用软引用构建缓存系统:
- 软引用非常适合用于缓存,因为它可以在内存紧张时自动释放缓存数据。
- 示例:
import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.Map; public class CacheManager { private final Map<String, SoftReference<MyObject>> cache = new HashMap<>(); public void put(String key, MyObject value) { cache.put(key, new SoftReference<>(value)); } public MyObject get(String key) { SoftReference<MyObject> ref = cache.get(key); return ref != null ? ref.get() : null; } public static void main(String[] args) { CacheManager cacheManager = new CacheManager(); cacheManager.put("key1", new MyObject()); MyObject obj = cacheManager.get("key1"); if (obj == null) { System.out.println("Cache was cleared due to memory pressure."); } else { System.out.println("Retrieved from cache: " + obj); } } }
监听器管理的最佳实践
- 使用弱引用避免内存泄漏:
- 使用弱引用可以避免由于监听器导致的内存泄漏问题。
- 示例:
import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; interface MyListener { void onEvent(); } class EventManager { private final List<WeakReference<MyListener>> listeners = new ArrayList<>(); public void addListener(MyListener listener) { listeners.add(new WeakReference<>(listener)); } public void notifyListeners() { List<WeakReference<MyListener>> toRemove = new ArrayList<>(); for (WeakReference<MyListener> ref : listeners) { MyListener listener = ref.get(); if (listener == null) { toRemove.add(ref); // 记录已被回收的监听器 } else { listener.onEvent(); } } listeners.removeAll(toRemove); // 移除已被回收的监听器 } }
总结
通过深入理解每种引用类型的特性和应用场景,开发者能够更好地管理内存,优化程序性能,并有效预防内存泄漏问题。每种引用类型都有其独特的用途,合理使用它们可以使应用程序更加高效和稳定:
- 强引用:适用于需要长期保持对象存活的场景。
- 软引用:适用于构建缓存系统,特别是在内存紧张时自动释放缓存数据。
- 弱引用:适用于规范化映射和监听器管理,防止内存泄漏。
- 虚引用:主要用于在对象被回收前执行特定操作,如清理资源。