【问题标题】:Logging when objects are garbage collected对象被垃圾回收时的日志记录
【发布时间】:2013-06-05 14:43:02
【问题描述】:

我的应用程序会记录某些对象的使用情况——我的设置使用 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
}

我的问题是这看起来很复杂,因为弱哈希映射不一定会清除其条目,除非需要空间,所以我可能会在收集对象后等待很长时间才能记录它。基本上,我正在寻找一种更好的方法来实现这一点。

注意 - 我正在监视任意对象并且无法控制它们的创建,因此无法覆盖它们的 finalize 方法。

【问题讨论】:

  • 听起来你真正想要的是reference queue
  • 所以我会用 ReferenceQueue 替换 WeakIdentityHashMap 和 ConcurrentLinkedQueue 但仍需要 GarbageLogger 线程来弹出收集的引用并记录它们?
  • 我使用了参考队列和并发映射方法,作为我以前做的事情的一种稍微整洁的方式。如果这被放在一个答案中,我会接受它。

标签: java garbage-collection weakhashmap


【解决方案1】:

响应垃圾回收事件的传统方式是将WeakReferences 注册到ReferenceQueue,当引用的对象被GC'd 时,它们将自动入队,然后定期轮询@ 987654323@(可能在单独的线程中)进行清理。

一个标准的技巧是扩展WeakReference类,附加任何你想在清理发生时知道的附加信息,然后将ReferenceQueue中的WeakReference对象转换回你的MyWeakReference以获得信息。

【讨论】:

    【解决方案2】:

    一个更简单的替代方案可能会产生更快的结果(并且也可以缓解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

    【讨论】:

    • 是的,同步地图上的瓶颈可能是个问题。您是否认为上述带有参考队列的解决方案(在评论中)也会成为瓶颈?...我想我必须在添加时同步。
    • @selig 引用队列不会出现瓶颈。 This code 可能会对您有所帮助 - 它结合了引用队列和并发映射
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多