我的应用程序记录了某些对象的使用情况 - 我的设置使用AspectJ来识别我感兴趣的上下文并记录这些用法。我稍后加载日志文件进行分析,但出于效率原因,了解何时无法再访问对象很有用。
我目前的方法是使用'垃圾记录器'记录我感兴趣的对象,然后创建一个包含对象标识哈希码的“saver”对象,并将其存储在弱哈希映射中。这个想法是,当收集对象时,将从弱哈希映射中移除保护对象并收集,从而运行代码以记录所收集对象的身份哈希码。我使用单独的线程和队列来防止在垃圾收集器中造成瓶颈。这是垃圾记录器代码:
public class GarbageLogger extends Thread {
private final Map<Object,Saver> Savings =
Collections.synchronizedMap(new WeakIdentityHashMap<Object,Saver>());
private final ConcurrentLinkedQueue<Integer> clearTheseHash =
new ConcurrentLinkedQueue<Integer>();
public void register(Object o){
Savings.put(o,new Saver(System.identityHashCode(o));
}
private class Saver{
public Saver(int hash){ this.hash=hash;}
private final int hash;
@Override
public void finalize(){
clearTheseHash.add(hash);
}
}
@Override
public void run(){
while(running){
if((clearTheseHash.peek() !=null)){
int h = clearTheseHash.poll();
log(h);
}
else sleep(100);
}
}
// logging and start/end code omitted
}
我的问题是,这似乎非常错综复杂,因为除非需要空间,否则弱哈希映射不一定会清除其条目,我可能会在收集对象之前等待很长时间才能记录它。基本上,我正在寻找一种更好的方法来实现这一目标。
注意 - 我正在监视任意对象,无法控制它们的创建,因此无法覆盖它们的终结方法。
答案 0 :(得分:6)
响应垃圾收集事件的传统方法是使用WeakReference
注册ReferenceQueue
,当引用的对象是GC时,它们将自动排入队列,然后到定期轮询ReferenceQueue
(可能在一个单独的线程中)进行清理。
标准技巧是扩展WeakReference
类,在清理发生时附加您想知道的任何其他信息,然后将WeakReference
中的ReferenceQueue
对象强制转换回您的MyWeakReference
获取信息。
答案 1 :(得分:3)
一个更简单的替代方案可以提供更快的结果(并且还可以缓解Savings
的潜在瓶颈)是
private final ConcurrentMap<WeakReference, Integer> savings = new ConcurrentHashMap<>();
public void run() {
while(running) {
for(WeakReference reference : savings.keySet()) {
if(reference.get() == null) {
log(savings.remove(reference));
}
}
sleep(1000);
}
}
缺点是你必须不断迭代地图才能找到清除的引用,但优点是实现更简单,只要清除了对象,reference.get() == null
就会成立(而可能是在WeakHashMap
中注册已清除对象的延迟。使用ConcurrentMap
可以缓解使用Collections.synchronizedMap
可能造成的瓶颈,更重要的是防止for-each循环抛出ConcurrentModificationException
。