在 C# 中如何知道弱引用对象是否将被垃圾收集?
In c# how to know if a weak referenced object is going to be garbage collected?
假设我有这样的代码:
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();
}
在这个例子中,我创建了一个新的对象实例并将其设置为 Test1
中的一个 WeakReference
实例。如果我在退出 Test1
后理解正确,则不会引用任何对象实例,因此该实例很快就会 GC
。
但是,在 Test2
中,如果 GC
没有执行,我仍然可以通过 m_ref.Target
.
访问对象实例
有什么方法可以知道 m_ref.Target
无效 而无需 手动执行 GC
?
Is there any way I could know that the m_ref.Target is invalid without manually perform the GC?
在GC回收对象之前不会无效。垃圾回收的意义在于你不知道也不必关心对象何时将被丢弃。
在你的例子中,是的,你是对的m_ref = new WeakReference(new object());
执行后,实例将被收集'soon'。但是,'soon' 没有具体定义,因此您不能假定这会在调用 Test2
和执行 Debug.Log(m_ref.Target);
之前发生。
不,你不能。按照设计,WeakReference
与垃圾收集器紧密耦合。甚至 documentation 也提到了它:
Gets an indication whether the object referenced by the current WeakReference object has been garbage collected.
据我所知,在 C# 中没有办法知道是否仍然存在对给定对象的引用,除非手动浏览整个引用树(并且几乎自己重新实现 GC)。
If I understand correctly after exit the Test1 there would be nothing referenced to the object instance ...
你错了。从技术上讲,您没有任何已创建对象的引用,它 可能 在下一行被 gc。
考虑这个简单的例子:
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
}
}
在Test()
中我们有一个对象的强引用,它确实持续到方法结束。
你可以做这样的事情来确定
object obj = _ref.Target;
if (obj != null)
{
... safe to do something with obj
}
您无法判断弱引用是否有效。但是你可以判断它是否无效。
我知道这很奇怪。如果我有执行 if 语句的代码并且可以询问 "is it valid" 那么它在下一行代码中可能无效,所以它是无用的。
对 WeakReference 的 TryGetTarget 调用获取对该对象的引用,或者失败并且 returns false。
一旦它有了引用,引用就会阻止对象被垃圾回收,所以至少只要你有引用,它就会保持有效。
在某些代码中,可能会保留一个 List>
跟踪该列表并清除未引用(其他地方)引用的一种好方法是让一些 for 循环使用 TryGetTarget 扫描列表,如果失败,则从列表中删除陈旧的引用。
但是这样的删除会破坏迭代器,因此您想将 RemoveAll 与谓词一起使用。
我有一个名为 CognateBase 的 class 和一个名为 AllCognateBases 的静态全局列表。
CognateBase 有一个 Tick() 函数,我每次在程序滴答时调用它。
Tick 循环是获取陈旧引用的好地方。所以我有...
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);
}
然后 _TickIfAble 是
private static bool _TickIfAble(WeakReference<CognateBase> r)
{
CognateBase cb;
if (r.TryGetTarget(out cb))
{
cb.Tick();
return false;
}
else
{
return true;
}
}
因此,有效的 CognateBase 实例会被勾选,不再有效的会被移除。而且因为它在 RemoveAll 中,所以没有迭代器会搞砸。
在我引用 CognateBase 并将其设置为 null 的代码中的任何其他地方,CognateBase 最终将被删除并从列表中移除。
这个测试有效。所有这些 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);
}
而创作者...
public CognateBase()
{
AllCognateBases.Add(new WeakReference<CognateBase>(this));
}
警告 - 当引用设置为 null 时,如上面的 b1 = null;这些对象可能在很长一段时间内都没有被垃圾回收。一直以来它仍然有效并且会得到它的 Tick 调用!
假设我有这样的代码:
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();
}
在这个例子中,我创建了一个新的对象实例并将其设置为 Test1
中的一个 WeakReference
实例。如果我在退出 Test1
后理解正确,则不会引用任何对象实例,因此该实例很快就会 GC
。
但是,在 Test2
中,如果 GC
没有执行,我仍然可以通过 m_ref.Target
.
有什么方法可以知道 m_ref.Target
无效 而无需 手动执行 GC
?
Is there any way I could know that the m_ref.Target is invalid without manually perform the GC?
在GC回收对象之前不会无效。垃圾回收的意义在于你不知道也不必关心对象何时将被丢弃。
在你的例子中,是的,你是对的m_ref = new WeakReference(new object());
执行后,实例将被收集'soon'。但是,'soon' 没有具体定义,因此您不能假定这会在调用 Test2
和执行 Debug.Log(m_ref.Target);
之前发生。
不,你不能。按照设计,WeakReference
与垃圾收集器紧密耦合。甚至 documentation 也提到了它:
Gets an indication whether the object referenced by the current WeakReference object has been garbage collected.
据我所知,在 C# 中没有办法知道是否仍然存在对给定对象的引用,除非手动浏览整个引用树(并且几乎自己重新实现 GC)。
If I understand correctly after exit the Test1 there would be nothing referenced to the object instance ...
你错了。从技术上讲,您没有任何已创建对象的引用,它 可能 在下一行被 gc。
考虑这个简单的例子:
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
}
}
在Test()
中我们有一个对象的强引用,它确实持续到方法结束。
你可以做这样的事情来确定
object obj = _ref.Target;
if (obj != null)
{
... safe to do something with obj
}
您无法判断弱引用是否有效。但是你可以判断它是否无效。 我知道这很奇怪。如果我有执行 if 语句的代码并且可以询问 "is it valid" 那么它在下一行代码中可能无效,所以它是无用的。 对 WeakReference 的 TryGetTarget 调用获取对该对象的引用,或者失败并且 returns false。 一旦它有了引用,引用就会阻止对象被垃圾回收,所以至少只要你有引用,它就会保持有效。
在某些代码中,可能会保留一个 List
跟踪该列表并清除未引用(其他地方)引用的一种好方法是让一些 for 循环使用 TryGetTarget 扫描列表,如果失败,则从列表中删除陈旧的引用。 但是这样的删除会破坏迭代器,因此您想将 RemoveAll 与谓词一起使用。 我有一个名为 CognateBase 的 class 和一个名为 AllCognateBases 的静态全局列表。 CognateBase 有一个 Tick() 函数,我每次在程序滴答时调用它。 Tick 循环是获取陈旧引用的好地方。所以我有...
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);
}
然后 _TickIfAble 是
private static bool _TickIfAble(WeakReference<CognateBase> r)
{
CognateBase cb;
if (r.TryGetTarget(out cb))
{
cb.Tick();
return false;
}
else
{
return true;
}
}
因此,有效的 CognateBase 实例会被勾选,不再有效的会被移除。而且因为它在 RemoveAll 中,所以没有迭代器会搞砸。
在我引用 CognateBase 并将其设置为 null 的代码中的任何其他地方,CognateBase 最终将被删除并从列表中移除。
这个测试有效。所有这些 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);
}
而创作者...
public CognateBase()
{
AllCognateBases.Add(new WeakReference<CognateBase>(this));
}
警告 - 当引用设置为 null 时,如上面的 b1 = null;这些对象可能在很长一段时间内都没有被垃圾回收。一直以来它仍然有效并且会得到它的 Tick 调用!