面试题答案
一键面试实现思路
- 确保对象无强引用:临时对象生命周期结束时,要保证在系统其他地方没有对其的强引用。只有当对象除了在WeakHashMap中作为键被弱引用外,不存在其他强引用,垃圾回收器才会在合适时机回收该对象。
- 合理设置引用关系:检查对象之间复杂的引用关系,避免因相互强引用导致对象无法被垃圾回收。可以通过将部分引用改为弱引用或软引用的方式,确保对象在没有外部强引用时能被回收。例如,如果对象A和对象B相互引用,将其中一个引用改为弱引用,如A对B的引用改为弱引用,当B没有其他强引用时,就可被垃圾回收。
- 定期清理WeakHashMap:虽然WeakHashMap会在键对象被回收时自动移除对应的键值对,但为了及时释放资源,可以定期手动清理WeakHashMap中已失效的键值对。可以使用
WeakHashMap
的entrySet()
方法遍历集合,通过Map.Entry
的getKey()
方法获取键,若键为null
,说明该键已被垃圾回收,应移除对应的键值对。
可能遇到的问题
- 复杂引用导致对象无法回收:对象间复杂的引用关系可能形成引用环,使得即使对象在业务层面不再使用,但因为环内的相互强引用,垃圾回收器无法回收这些对象。
- WeakHashMap清理不及时:如果没有及时手动清理WeakHashMap,已失效的键值对会一直占用内存,可能导致内存占用过高。虽然WeakHashMap本身会在键被回收时移除键值对,但在垃圾回收器未及时回收键对象的情况下,资源无法及时释放。
解决方案
- 打破引用环:分析对象间的引用关系,找到并打破引用环。可以在合适的时机,如对象生命周期结束前,将环内的某个强引用改为弱引用或软引用。例如,在对象不再使用时,将对象A对对象B的引用设置为
null
,打破循环引用。 - 定时任务清理WeakHashMap:通过定时任务(如使用
ScheduledExecutorService
),定期检查WeakHashMap,移除键为null
的键值对。示例代码如下:
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class WeakHashMapCleaner {
private static final WeakHashMap<Object, Object> weakHashMap = new WeakHashMap<>();
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
static {
scheduler.scheduleAtFixedRate(() -> {
weakHashMap.entrySet().removeIf(entry -> entry.getKey() == null);
}, 0, 1, TimeUnit.MINUTES);
}
public static void main(String[] args) {
// 添加键值对到WeakHashMap
Object key = new Object();
weakHashMap.put(key, "value");
// 模拟键对象失去强引用
key = null;
// 等待一段时间,观察WeakHashMap是否清理
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("WeakHashMap size: " + weakHashMap.size());
}
}
此代码通过ScheduledExecutorService
设置了每分钟执行一次清理任务,检查并移除WeakHashMap中键为null
的键值对。