【问题标题】:Is it possible to hook objects being collected by GC?是否可以挂钩由 GC 收集的对象?
【发布时间】:2011-12-28 10:38:28
【问题描述】:

假设我有一个目标强引用的WeakReference。我想在目标对象本身被 GC 收集时得到通知。有可能吗?

编辑:向终结器/析构函数添加代码不是这里的选项。我需要一些不依赖于类代码的东西。

【问题讨论】:

  • 我不相信有任何事件表明正在收集地址。您可能尝试的最接近的事情是在被引用的类型上创建一个终结器,尽管它们在收集发生之前的未指定时间之前运行。我期待有更聪明的人给出答案。
  • 如果垃圾收集器销毁对象,则调用对象的析构函数。例如,您可以在析构函数调用中做出反应并发送一个自制事件。
  • 听起来像是复活的秘诀。为什么需要这个?
  • 抱歉,我需要一些更透明且不依赖于类代码的东西。也许没有黑客是不可能的。我需要它来跟踪自定义序列化框架中对象的分离实例:收集对象时,我想清理Guid WeakReference 的静态映射。如果没有这个,我可以通过在延迟时间手动测试弱引用来检查对象是否被收集。对象实现了一个接口,因此框架更加灵活,因此添加终结器不是一种选择。

标签: c# .net garbage-collection weak-references strong-references


【解决方案1】:

可以在 .NET 4.0 下使用 ConditionalWeakTable<TKey, TValue>。感谢thisother 网站。它遵循概念验证代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace Test
{
    public static class GCInterceptor
    {
        private static ConditionalWeakTable<object, CallbackRef> _table;

        static GCInterceptor()
        {
            _table = new ConditionalWeakTable<object, CallbackRef>();
        }

        public static void RegisterGCEvent(this object obj, Action<int> action)
        {
            CallbackRef callbackRef;
            bool found = _table.TryGetValue(obj, out callbackRef);
            if (found)
            {
                callbackRef.Collected += action;
                return;
            }

            int hashCode = RuntimeHelpers.GetHashCode(obj);
            callbackRef = new CallbackRef(hashCode);
            callbackRef.Collected += action;
            _table.Add(obj, callbackRef);
        }

        public static void DeregisterGCEvent(this object obj, Action<int> action)
        {
            CallbackRef callbackRef;
            bool found = _table.TryGetValue(obj, out callbackRef);
            if (!found)
                throw new Exception("No events registered");

            callbackRef.Collected -= action;
        }

        private class CallbackRef
        {
            private int _hashCode;
            public event Action<int> Collected;

            public CallbackRef(int hashCode)
            {
                _hashCode = hashCode;
            }

            ~CallbackRef()
            {
                Action<int> handle = Collected;
                if (handle != null)
                    handle(_hashCode);
            }
        }
    }
}

用以下代码测试:

public partial class Form1 : Form
{
    private object _obj;

    public Form1()
    {
        InitializeComponent();

        _obj = new object();

        _obj.RegisterGCEvent(delegate(int hashCode)
        {
            MessageBox.Show("Object with hash code " + hashCode + " recently collected");
        });
    }

    private void button1_Click(object sender, EventArgs e)
    {
        _obj = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

【讨论】:

  • 有效,但请注意 GC.Collect 不足以强制您执行 GC.Waitfinalizers。
  • 编辑了答案。现在可以了吗?
【解决方案2】:

Object.Finalize() 方法呢?这不会在完成时调用吗?

【讨论】:

  • 这是正确的。析构函数隐式调用Object.Finalize(),因此重写此方法并从此处发出信号将给出对象被垃圾回收的确切时间。
  • 抱歉,我需要一些更透明且不依赖于类代码的东西。也许这是不可能的。
  • 除非调用GC.SupressFinalize,否则此方法有效,Disposeable 类型通常是这种情况。此外,在垃圾回收期间终结器执行的确切时间是未定义的。
  • AOP 呢?也许它会以某种方式帮助你。
【解决方案3】:

您可以使用拦截来捕获从自定义接口/类继承的每个类的 Finalize。我想,这就是你想要达到的目标,对吧?您可以为此使用 Unity。 Here 是一个非常简短的示例,如何使用 Unity 进行拦截。

【讨论】:

  • 正确(其实它是一个接口,所以解释了为什么我不能只在基类终结器中编写代码)。 Unity 是 .NET 3.5 中内置的吗?
  • 根据here,它也支持.NET 3.5 SP1。但是,我在 .NET 4.0 中使用过它。此外,网络上还有其他 IoC 库。但是,我发现 Unity 是更强大的解决方案。
  • 您能否向我确认所有 Unity 方法(TransparentProxyInterceptor、InterfaceInterceptor、VirtualMethodInterceptor)都需要使用代理类?不幸的是,在这种情况下,对我现有代码的更改太具有侵入性了。
  • 我的意思是:var repository = Container.Instance.Resolve&lt;CustomerRepository&gt;(); 实际上没有使用代理引用?我不能只有一个引用,不管我是如何得到它的(构造函数、手动分配加上反射初始化......)并为该引用或类的所有引用上的方法调用注入代码?
  • 解析方法依赖于 Unity 容器中的注册类型。因此,您无法解析任何仅作为参考的类。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-10
  • 2013-12-29
  • 1970-01-01
  • 2016-08-02
  • 1970-01-01
  • 2010-11-17
相关资源
最近更新 更多