简体   繁体   English

在C#中如何知道是否弱引用对象将被垃圾回收?

[英]In c# how to know if a weak referenced object is going to be garbage collected?

Suppose I have such code: 假设我有这样的代码:

class Test
{
    WeakReference m_ref;

    public Test()
    {
        Test1();
        Test2();
    }

    void Test1()
    {
        m_ref = new WeakReference(new object());
    }

    void Test2()
    {
        // If I do the GC then the m_ref.Target is null
        // GC.Collect();
        Debug.Log(m_ref.Target);
    }
}

void TestFunc()
{
    new Test();
}

In this example I created a new object instance and set it to a WeakReference instance in Test1 . 在此示例中,我创建了一个新的对象实例,并将其设置为Test1WeakReference实例。 If I understand correctly after exit the Test1 there would be nothing referenced to the object instance so this instance would be GC soon. 如果我在退出Test1后正确理解该对象实例,则不会引用任何内容,因此该实例将很快成为GC

However, in the Test2 if GC is not performed I can still access the object instance via m_ref.Target . 但是,在Test2如果未执行GC我仍然可以通过m_ref.Target访问对象实例。

Is there any way I could know that the m_ref.Target is invalid without manually perform the GC ? 我有什么办法可以知道,如果手动执行GCm_ref.Target是无效的?

Nope, you can't. 不,你不能。 By design, WeakReference is tightly coupled to the garbage collector. 根据设计, WeakReference与垃圾收集器紧密耦合。 Even the documentation mentions it: 甚至文档也提到了这一点:

Gets an indication whether the object referenced by the current WeakReference object has been garbage collected. 获取当前WeakReference对象引用的对象是否已被垃圾回收的指示

As far as I know, there's no way in C# to know whether there's still alive references to a given object, except maybe manually browsing the whole reference tree (and pretty much reimplementing the GC yourself). 据我所知,C#中没有办法知道是否仍然存在对给定对象的引用,除非手动浏览整个引用树(并自己重新实现GC)。

Is there any way I could know that the m_ref.Target is invalid without manually perform the GC? 如果不手动执行GC,有什么方法可以知道m_ref.Target无效吗?

It is not invalid until the GC collects the object. 直到GC收集的对象,是不是无效。 The point of garbage collection is that you don't know and you don't have to take care of when the object is going to be discarded. 垃圾收集的重点是您不知道,也不必照顾何时丢弃该对象。

In your example, yes, you are right that after m_ref = new WeakReference(new object()); 在您的示例中,是的,在m_ref = new WeakReference(new object());之后,您是对的m_ref = new WeakReference(new object()); is executed, the instance will be collected 'soon'. 执行后,实例将很快被收集。 However, 'soon' is not defined specifically whatsoever, so you cannot presume this will happen before Test2 is invoked and Debug.Log(m_ref.Target); 但是,'soon'并没有明确定义,因此您不能假定这会在调用Test2Debug.Log(m_ref.Target);之前发生Debug.Log(m_ref.Target); executed. 被执行。

If I understand correctly after exit the Test1 there would be nothing referenced to the object instance ... 如果我在退出Test1后正确理解,将没有对对象实例的引用...

You are wrong. 你错了。 Technically you don't have any reference of created object, it may be gc'ed on the next line. 从技术上讲,您没有创建对象的任何引用, 可以在下一行进行gc'ed。

Consider this simple example: 考虑以下简单示例:

class Program
{
    static WeakReference _ref;

    static void Main(string[] args)
    {
        Test();
        GC.Collect();
        Console.WriteLine(_ref.IsAlive); // false
    }

    static void Test()
    {
        var obj = new object();
        _ref = new WeakReference(obj);
        GC.Collect();
        Console.WriteLine(_ref.IsAlive); // true
    }
}

In Test() we have strong reference to an object, it indeed persist until end of method. Test()我们对对象有很强的引用,它确实会持续到方法结束。

You can do something like this to be sure 您可以这样做,以确保

object obj = _ref.Target;
if (obj != null)
{
    ... safe to do something with obj
}

You can not tell if a WeakReference is valid. 您无法确定WeakReference是否有效。 But you can tell if it is invalid. 但是您可以判断它是否无效。 I know that is strange. 我知道那很奇怪。 If I have code that does an if statement and could ask "is it valid" then it could be invalid on the next line of code, so it is useless. 如果我有执行if语句的代码并且可以询问“它是否有效”,那么它在下一行代码中可能无效,因此它是无用的。 The TryGetTarget call on WeakReference gets a reference to the object, or fails and returns false. 对WeakReference的TryGetTarget调用获取对该对象的引用,否则将失败并返回false。 Once it has a reference, the reference prevents the object from being garbage collected, so it will stay valid at least as long as you have the reference. 一旦有了引用,引用就可以防止对象被垃圾回收,因此它至少在您具有引用的情况下才有效。

In some code one might be keeping a List<WeakReference<MyNiftyClass>> 在某些代码中,可能会保留一个List <WeakReference <MyNiftyClass >>

One great way to keep track of that list and keep it clean of unreferenced (elsewhere) references is to have some for loop scan the list with TryGetTarget and if it fails remove the stale reference from the list. 跟踪该列表并清除未引用(其他位置)引用的一种好方法是使用TryGetTarget对列表进行一些for循环扫描,如果失败,则从列表中删除陈旧的引用。 But a removal like that wrecks the iterator so you want to use RemoveAll with a predicate. 但是像这样的删除会破坏迭代器,因此您要对谓词使用RemoveAll。 I have a class called CognateBase and a static global list called AllCognateBases. 我有一个名为CognateBase的类和一个名为AllCognateBases的静态全局列表。 The CognateBase has a Tick() function I call every time tick of the program. CognateBase有一个Tick()函数,每次程序滴答时都会调用一次。 The Tick loop is a good place to reap stale references. Tick循环是获取陈旧引用的好地方。 So I have... 所以我有...

    public static void TickAll()
    {
        // This will loop through all CognateBase objects and call their Tick, or if deleted from memory, remove the CognateBase.
        AllCognateBases.RemoveAll(_TickIfAble);
    }

And then the _TickIfAble is 然后_TickIfAble是

private static bool _TickIfAble(WeakReference<CognateBase> r)
    {
        CognateBase cb;
        if (r.TryGetTarget(out cb))
        {
            cb.Tick();
            return false;
        }
        else
        {
            return true;
        }
    }

Thus the CognateBase instances that are valid get ticked, and the ones that are not valid any more get removed. 因此,有效的CognateBase实例将被打勾,无效的实例将被删除。 And because it is in RemoveAll there is no iterator to get messed up. 而且因为它在RemoveAll中,所以没有搞砸的迭代器。

Anywhere else in the code where I have a reference to a CognateBase and set it to null, the CognateBase will eventually get deleted and removed from the list. 在代码中我可以引用CognateBase并将其设置为null的任何其他位置,CognateBase最终都将被删除并从列表中删除。

And this test works. 并且此测试有效。 All those GC calls are to force Garbage Collection to happen NOW not some time later when C# feels like it. 所有这些GC调用都是为了强制垃圾回收立即发生,而不是不久之后C#感觉到它。

        public static void UnitTest()
    {
        Debug.Assert(AllCognateBases.Count == 0);
        CognateBase b1 = new CognateBase();
        Debug.Assert(AllCognateBases.Count == 1);
        CognateBase b2 = new CognateBase();
        Debug.Assert(AllCognateBases.Count == 2);
        GC.Collect();
        Debug.Assert(AllCognateBases.Count == 2);

        b1 = null;
        GC.Collect();
        GC.WaitForFullGCComplete();
        GC.WaitForPendingFinalizers();
        TickAll();
        GC.Collect();
        GC.WaitForFullGCComplete();
        GC.WaitForPendingFinalizers();
        Debug.Assert(AllCognateBases.Count == 1);

        b2 = null;
        GC.Collect();
        GC.WaitForFullGCComplete();
        GC.WaitForPendingFinalizers();
        TickAll();
        GC.Collect();
        GC.WaitForFullGCComplete();
        GC.WaitForPendingFinalizers();
        Debug.Assert(AllCognateBases.Count == 0);
    }

And the creator... 还有创造者...

       public CognateBase()
    {
        AllCognateBases.Add(new WeakReference<CognateBase>(this));
    }

WARNING - WHen a reference is set to null like the above b1 = null; 警告-像上面的b1 = null一样,引用被设置为null。 the objects may not get garbage collected for a looooooong time. 这些对象可能无法在很长一段时间内收集到垃圾。 All that time it is still valid and will get it's Tick called! 一直以来,它仍然有效,并且会被称为Tick!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM