C# 弱引用在对象 GC 后仍然有效?
C# weak reference still valid after object GC?
我有一个测试弱引用的快速代码片段,我希望在对象被 GC 之后,弱引用不应再 return 对象引用。但我的测试表明这不是预期的:
class Person
{
private int mI = 3;
public int MI { get => mI; set => mI = value; }
}
class UseWeakReference
{
public static void Main(String[] args)
{
Person person = new Person();
WeakReference<Person> wr = new WeakReference<Person>(person);
wr.TryGetTarget(out Person p1);
Console.WriteLine(p1);
person = null;
wr.TryGetTarget(out Person p2);
Console.WriteLine(p2);
p2 = null;
System.GC.Collect();
Thread.Sleep(1000);
wr.TryGetTarget(out Person p3);
Console.WriteLine(p3); // I expected null here becaure person is collected.
}
}
它打印:
MyApp1.Person
MyApp1.Person
MyApp1.Person // Why still valid?
我哪里错了?
谢谢。
当您在弱引用上调用 TryGetTarget
时,假定引用的对象尚未被收集,您将返回对该对象的强引用。您在代码中执行了 3 次:p1
、p2
和 p3
是对该对象的强引用。当垃圾收集器运行时 - 自动运行或在您强制垃圾收集的情况下 - 那些强引用将阻止收集对象。
这是一个有效的版本:
void Main()
{
var person = new Person();
WeakReference<Person> weak = new WeakReference<Person>(person);
person = null;
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"{i}\t{TestReference(weak)}");
Thread.Sleep(100);
}
GC.Collect();
Console.WriteLine(TestReference(weak));
}
class Person
{
private int mI = 3;
public int MI { get => mI; set => mI = value; }
}
bool TestReference(WeakReference<Person> weak)
{
if (weak.TryGetTarget(out Person p))
{
p = null;
return true;
}
return false;
}
请注意,在任何时候我们都不会将对象的强引用保持存活超过几个周期,并且在垃圾收集器运行时没有对该对象的强引用,因此该对象已被收集。
即使在这段代码中,如果我注释掉 p = null;
行,垃圾收集器 可能不会收集该对象。 试试看。
这个故事的寓意是:当您从 WeakReference<>
获得强引用时,总是 在您完成强引用时将其置空。
我有一个测试弱引用的快速代码片段,我希望在对象被 GC 之后,弱引用不应再 return 对象引用。但我的测试表明这不是预期的:
class Person
{
private int mI = 3;
public int MI { get => mI; set => mI = value; }
}
class UseWeakReference
{
public static void Main(String[] args)
{
Person person = new Person();
WeakReference<Person> wr = new WeakReference<Person>(person);
wr.TryGetTarget(out Person p1);
Console.WriteLine(p1);
person = null;
wr.TryGetTarget(out Person p2);
Console.WriteLine(p2);
p2 = null;
System.GC.Collect();
Thread.Sleep(1000);
wr.TryGetTarget(out Person p3);
Console.WriteLine(p3); // I expected null here becaure person is collected.
}
}
它打印:
MyApp1.Person
MyApp1.Person
MyApp1.Person // Why still valid?
我哪里错了?
谢谢。
当您在弱引用上调用 TryGetTarget
时,假定引用的对象尚未被收集,您将返回对该对象的强引用。您在代码中执行了 3 次:p1
、p2
和 p3
是对该对象的强引用。当垃圾收集器运行时 - 自动运行或在您强制垃圾收集的情况下 - 那些强引用将阻止收集对象。
这是一个有效的版本:
void Main()
{
var person = new Person();
WeakReference<Person> weak = new WeakReference<Person>(person);
person = null;
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"{i}\t{TestReference(weak)}");
Thread.Sleep(100);
}
GC.Collect();
Console.WriteLine(TestReference(weak));
}
class Person
{
private int mI = 3;
public int MI { get => mI; set => mI = value; }
}
bool TestReference(WeakReference<Person> weak)
{
if (weak.TryGetTarget(out Person p))
{
p = null;
return true;
}
return false;
}
请注意,在任何时候我们都不会将对象的强引用保持存活超过几个周期,并且在垃圾收集器运行时没有对该对象的强引用,因此该对象已被收集。
即使在这段代码中,如果我注释掉 p = null;
行,垃圾收集器 可能不会收集该对象。 试试看。
这个故事的寓意是:当您从 WeakReference<>
获得强引用时,总是 在您完成强引用时将其置空。