You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

泛型与非泛型WeakReference的行为差异排查

Why Generic WeakReference<T> and Non-Generic WeakReference Behave Differently in Your GC Tests

The core difference you're seeing doesn't stem from the WeakReference<T> class itself, but from how your test code interacts with it combined with JIT compiler optimizations and GC collection heuristics. Here's a detailed breakdown:

1. Key Test Code Difference

Your non-generic tests use the IsAlive property, which checks if the target is still alive without creating a strong reference to it. In contrast, your generic tests use TryGetTarget(out var _), which retrieves the target into a local variable (even though it's a discard) — this creates a temporary strong reference to the target object.

2. JIT Optimization and Variable Scope

Even though you discard the out variable, the JIT compiler may not immediately optimize it away. The variable remains in scope until the end of the test method, meaning the target object is technically still reachable when you call GC.Collect().

3. GC Collection Mode Behavior

  • GCCollectionMode.Forced: This mode forces the GC to collect eligible objects regardless of heuristics. It ignores the temporary strong reference from the discard variable and collects the target as expected.
  • GCCollectionMode.Optimized: This mode lets the GC use heuristics to decide whether collecting an object is worth the overhead. If the JIT hasn't optimized away the discard variable, the GC sees the target as still reachable (via the strong reference) and skips collecting it.

4. Why the Non-Generic Test Passes

Since the non-generic test doesn't create any strong reference to the target after the weak reference is created, the object is immediately eligible for collection. The GC collects it even in Optimized mode because there are no strong references keeping it alive.

Fixing the Generic Tests

To make your generic tests behave consistently with the non-generic ones, you need to ensure the temporary strong reference from TryGetTarget goes out of scope before calling GC.Collect():

[TestCase(2, GCCollectionMode.Optimized, true)]
public void TestGenericWeakReferenceWithObject(int generation, GCCollectionMode forced, bool blocking)
{
    static WeakReference<object> CreateWeakReference()
    {
        return new WeakReference<object>(new object(), trackResurrection);
    }
    var x = CreateWeakReference();
    // Wrap the TryGetTarget call in a block to limit the discard variable's scope
    {
        Assert.IsTrue(x.TryGetTarget(out var _));
    }
    GC.Collect(generation, forced, blocking);
    // Optional: Wait for finalizers and collect again to ensure full cleanup
    GC.WaitForPendingFinalizers();
    GC.Collect(generation, forced, blocking);
    Assert.IsFalse(x.TryGetTarget(out var _));
}

Additional Notes

  • Both WeakReference and WeakReference<T> use the same underlying GCHandle mechanism for weak references when tracking reference types. The behavior difference is purely a side effect of your test code's interaction with the classes.
  • Calling GC.WaitForPendingFinalizers() followed by a second GC.Collect() can help ensure objects marked for collection are fully cleaned up, especially if they have finalizers.

内容的提问来源于stack exchange,提问作者Andreas Synnerdahl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 23:07:33