.NET 6与.NET Framework中WeakHandle的GC行为差异及原因问询
.NET 6中WeakHandle引用对象未被GC回收的原因分析
现象描述
对WeakHandle进行实验时,发现了.NET 6中的特殊行为:
以下代码在**Release模式编译(避免提前根回收)**的.NET 6环境中,输出结果为System.Int32[];但在.NET Framework 4.7.2中,Release模式下输出null,Debug模式下输出System.Int32[],LinqPad中开启/关闭优化测试也得到相同的跨框架差异结果。
static void Main(string[] args) { var foo = new int[3]; var fooWeakHandle = GCHandle.Alloc(foo, GCHandleType.Weak); GC.Collect(); Console.WriteLine(fooWeakHandle.Target); }
补充测试验证
即使按照建议将代码移至独立方法,并添加MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining标记,结果依旧相同;替换为自定义空类B的实例,现象也完全一致:
static void Main(string[] args) { DifferentFunction(); } [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] static void DifferentFunction() { var foo = new int[3]; var fooWeakHandle = GCHandle.Alloc(foo, GCHandleType.Weak); GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true); Console.WriteLine(fooWeakHandle.Target); } class B { }
对应的IL代码(仅框架引用有差异,逻辑完全一致):
.method private hidebysig static void DifferentFunction () cil managed noinlining nooptimization { // Method begins at RVA 0x2058 // Header size: 12 // Code size: 35 (0x23) .maxstack 4 .locals init ( [0] valuetype [System.Runtime]System.Runtime.InteropServices.GCHandle fooWeakHandle ) // GCHandle gCHandle = GCHandle.Alloc(new int[3], GCHandleType.Weak); IL_0000: ldc.i4.3 IL_0001: newarr [System.Runtime]System.Int32 IL_0006: ldc.i4.0 IL_0007: call valuetype [System.Runtime]System.Runtime.InteropServices.GCHandle [System.Runtime]System.Runtime.InteropServices.GCHandle::Alloc(object, valuetype [System.Runtime]System.Runtime.InteropServices.GCHandleType) IL_000c: stloc.0 // GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true); IL_000d: ldc.i4.2 IL_000e: ldc.i4.1 IL_000f: ldc.i4.1 IL_0010: ldc.i4.1 IL_0011: call void [System.Runtime]System.GC::Collect(int32, valuetype [System.Runtime]System.GCCollectionMode, bool, bool) // Console.WriteLine(gCHandle.Target); IL_0016: ldloca.s 0 IL_0018: call instance object [System.Runtime]System.Runtime.InteropServices.GCHandle::get_Target() IL_001d: call void [System.Console]System.Console::WriteLine(object) // } IL_0022: ret } // end of method Program::DifferentFunction
原因分析
这是.NET 6及后续版本GC对短生命周期对象的优化逻辑导致的:
- .NET Framework中,对象一旦失去所有强引用,即使方法未执行完毕,触发GC时也会回收仅被弱引用的对象。
- .NET 6+的GC会将方法内刚创建的短生命周期对象分配到栈内存区域(或类似临时区域),或者通过JIT编译优化延长了对象的生命周期——即使没有显式强引用,GC也不会在方法执行过程中回收这些对象,直到方法退出。
- 另外,虽然IL代码中没有显式保留强引用,但.NET 6的JIT可能在底层保留了对象的临时引用,导致GC判定对象仍有根引用存在,因此不执行回收。
需要注意:这种行为属于GC的实现细节,不同版本、不同Runtime可能存在差异,不能依赖该行为编写业务代码——弱引用的设计初衷就是允许对象在无强引用时被回收。
内容的提问来源于stack exchange,提问作者Ivan Petrov
相关产品推荐
相关产品推荐

