【问题标题】:Does Action<T> prevent enclosing class instance to be GCed when all other references to class instance are removed?当所有其他对类实例的引用都被删除时,Action<T> 是否会阻止封闭类实例被 GC?
【发布时间】:2015-09-17 09:41:41
【问题描述】:

我想知道Action&lt;T&gt; 是否会在运行时删除对此类实例的所有其他引用时阻止对封闭类实例进行垃圾收集?

public class Class1
{
    private Action<string> _callback;

    public Class1(Action<string> callback)
    {
        _callback = callback;
    }

    public void DoSomething(string msg)
    {
        _callback(msg);
    }

}

public class Class2
{
    private List<Class1> _class1s;

    public Class2()
    {
        _class1s = new List<Class1>();
    }

    public void AddClass1Instance()
    {
        _class1s.Add(new Class1(OnClass1DoSomething));
    }

    public void RemoveLastClass1Instance()
    {
        if(_class1s.Count > 0)
        {
            _class1s.RemoveAt(_class1s.Count - 1);
        }
    }

    private void OnClass1DoSomething(string msg)
    {

    }

}

在这个简单的例子中,当我在Class2 中调用RemoveLastClass1Instance() 时,Class1 实例是否会被垃圾回收,还是通过Action&lt;string&gt; 保持对OnClass1DoSomething() 的引用?我的目标是完全删除 Class1 实例并将它们收集为垃圾。

编辑:(为了更好地理解 Jon Skeet 的 cmets,我添加了以下代码

public class Class1
{
    public event Action<string> Event;

    public Class1()
    {

    }

    public void DoSomething(string msg)
    {
        var handle = Event;
        if (handle != null)
        {
            handle(msg);
        }
    }

}

public class Class2
{
    private List<Class1> _class1s;

    public Class2()
    {
        _class1s = new List<Class1>();
    }

    public void AddClass1Instance()
    {
        var newClass1Instance = new Class1();
        newClass1Instance.Event += OnClass1DoSomething;
        _class1s.Add(newClass1Instance);
    }

    public void RemoveLastClass1Instance()
    {
        if(_class1s.Count > 0)
        {
            _class1s.RemoveAt(_class1s.Count - 1);
        }
    }

    private void OnClass1DoSomething(string msg)
    {

    }

}

【问题讨论】:

  • OnClass1DoSomething 创建一个Action&lt;string&gt; 将创建一个委托维护对Class2 实例的引用,而不是Class1...
  • 当然,Class1 对象将继续引用 Class2。但是 GC 并不关心这些,它会查找对 Class1 对象的引用。由于您删除了存储在 List 中的唯一引用,因此没有剩余引用,它将被收集。同样,必须有对 Class2 对象的引用。如果没有(Class1 对象持有的引用不够),则 ​​Class2 和 all Class1 对象都将被收集。

标签: c# reference callback garbage-collection


【解决方案1】:

我想知道Action&lt;T&gt; 是否会阻止在运行时删除对此类实例的所有其他引用时对封闭类实例进行垃圾回收?

没有。如果委托实例仍然在其他地方被引用,并且它有 Class1 实例的目标,那么它将阻止 Class1 的实例被垃圾收集 - 但事实上,目标Action&lt;string&gt; 的一个实例是 Class2

基本上,在垃圾回收方面,委托并没有什么神奇之处。如果委托引用实例方法,则该实例的目标作为引用保存......因此,如果委托仍然可以访问,则委托的目标仍然可以访问。在您的情况下,委托本身是不可访问的,您显然并不担心 Class2 实例是否可以被垃圾收集。

使用事件的更改根本不会影响这一点 - 它仍然是 Class1 的实例具有对 Class2 原始实例的引用(通过委托)... 只有 通过列表引用来自 Class2Class1 实例。

【讨论】:

  • 谢谢,那为什么我必须先取消订阅事件,才能对封闭的类实例进行 GC?例如,如果我将一个公共事件作为Class1 的一部分公开并在Class2 内订阅它,我的理解是我必须先取消订阅才能对Class1 实例进行GC,对吗?
  • @MattWolf: 不,反过来——你必须在 Class2 实例被垃圾回收之前取消订阅事件,因为 Class1 将引用一个委托,而委托引用了 Class2 的实例。
  • 我明白了,但我现在并不关心 Class2 实例,只关心删除和收集 Class1 实例垃圾。
  • @MattWolf:我不确定你是否“明白”——我的评论与你之前的评论直接矛盾。您现在是否明白您对事件的表达理解(Class2 将使 Class1 保持活动状态,其中 Class1 是事件发布者)是不正确的?
  • 我添加了另一个代码sn-p,所以当我删除RemoveLastClass1Instance() 中的Class1 实例时,无论我是否取消订阅Class1 中的事件,该实例最终都会被垃圾回收?顺便说一句,我的最后一条评论并不是我对您的评论的误解,我说我知道代表的问题,但我一直认为事件的情况会有所不同......
猜你喜欢
  • 1970-01-01
  • 2016-08-03
  • 2013-07-06
  • 2015-04-24
  • 2016-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-18
相关资源
最近更新 更多