How/Why 为实例成员引用 return
How/Why does ref return for instance members
我正在尝试理解 why/how 对 class 成员的 return 引用的情况下 ref-return。
换句话说,我想从 CLR 的内存安全方面了解保证实例成员的 ref-return 起作用的运行时的内部工作原理。
我所指的具体功能在 ref-return documentation 中提到,具体说明:
The return value cannot be a local variable in the method that returns it; it must have a scope that is outside the method that returns it. It can be an instance or static field of a class, or it can be an argument passed to the method. Attempting to return a local variable generates compiler error CS8168, "Cannot return local 'obj' by reference because it is not a ref local."
这是一个代码片段,可以干净地编译和运行并演示 returning 一个实例字段作为 ref return:
using System;
using System.Diagnostics;
namespace refreturn
{
public struct SomeStruct {
public int X1;
}
public class SomeClass {
SomeStruct _s;
public ref SomeStruct S => ref _s;
}
class Program
{
static void Main(string[] args)
{
var x = new SomeClass();
// This will store a direct pointer to x.S
ref var s = ref x.S;
// And now the GC will be free to re-use this memory
x = null;
// Why should s.X1 be considered safe?
Console.WriteLine(s.X1 + 0x666);
}
}
}
我对这段代码的问题是:GC 中的底层机制是什么,可确保它在最后一次对 SomeClass
实例的引用据称设置为 null 后继续跟踪它?
更准确地说...:
假设本地 s
存储了一个指向 SomeClass 实例的 _s
成员的直接指针(从下面的 windbg 反汇编),其最后的 "explicit" 引用在下一行 (x = null
),GC 如何跟踪 SomeClass
实例的 live-root 以防止此程序崩溃...?
Windbg 反汇编:
007ff9`e01504dc e8affbffff call 00007ff9`e0150090 (refreturn.SomeClass.get_S(), mdToken: 0000000006000001)
//rbp-30h stores the pointer to the struct
00007ff9`e01504e1 488945d0 mov qword ptr [rbp-30h],rax
// Now it's copied to rcx
00007ff9`e01504e5 488b4dd0 mov rcx,qword ptr [rbp-30h]
// And now copied to rbp-20h
00007ff9`e01504e9 48894de0 mov qword ptr [rbp-20h],rcx
00007ff9`e01504ed 33c9 xor ecx,ecx
// The last reference is overwritten with null
00007ff9`e01504ef 48894de8 mov qword ptr [rbp-18h],rcx
// rbp-20h is copied to rcx again
00007ff9`e01504f3 488b4de0 mov rcx,qword ptr [rbp-20h]
// Isn't this a possible boom?!?!?
00007ff9`e01504f7 8b09 mov ecx,dword ptr [rcx]
00007ff9`e01504f9 81c19a020000 add ecx,29Ah
00007ff9`e01504ff e85c634c5d call mscorlib_ni+0xd56860 (00007ffa`3d616860) (System.Console.WriteLine(Int32), mdToken: 0000000006000b5b)
00007ff9`e0150504 90 nop
How does the .NET GC / runtime make sure that this would never result in an access violation or reading wild pointers after the SomeClass backing memory has been re-used for something else?
出于与 x
不会引用已被 GC 清理的对象完全相同的原因。您知道这一点是因为 GC 不允许清理可通过托管代码访问的对象。在您清除它之前,可以通过 x
访问该对象,也可以通过 s
访问该对象。 s
是对该对象的引用,GC 在确定是否允许收集该对象时需要对其进行处理。
// This will store a direct pointer to x.S
ref var s = ref x.S;
它存储一个指向堆变量的托管内部指针;指针存储到短期存储上的某个位置。短期存储是 GC 的根。
// And now the GC will be free to re-use this memory
x = null;
天哪,没有。 在 GC root 中有一个活的托管内部指针。
How does the .NET GC / runtime make sure that this would never result in an access violation or reading wild pointers after the SomeClass backing memory has been re-used for something else?
直到内部托管指针不再是 GC 的根之后才释放该内存。或者,换句话说:通过实施正确的垃圾收集器。
我不知道你在这里真正问的是什么问题。 GC 阻止了错误 因为这是它唯一的工作 并且它已正确实施。
我正在尝试理解 why/how 对 class 成员的 return 引用的情况下 ref-return。 换句话说,我想从 CLR 的内存安全方面了解保证实例成员的 ref-return 起作用的运行时的内部工作原理。
我所指的具体功能在 ref-return documentation 中提到,具体说明:
The return value cannot be a local variable in the method that returns it; it must have a scope that is outside the method that returns it. It can be an instance or static field of a class, or it can be an argument passed to the method. Attempting to return a local variable generates compiler error CS8168, "Cannot return local 'obj' by reference because it is not a ref local."
这是一个代码片段,可以干净地编译和运行并演示 returning 一个实例字段作为 ref return:
using System;
using System.Diagnostics;
namespace refreturn
{
public struct SomeStruct {
public int X1;
}
public class SomeClass {
SomeStruct _s;
public ref SomeStruct S => ref _s;
}
class Program
{
static void Main(string[] args)
{
var x = new SomeClass();
// This will store a direct pointer to x.S
ref var s = ref x.S;
// And now the GC will be free to re-use this memory
x = null;
// Why should s.X1 be considered safe?
Console.WriteLine(s.X1 + 0x666);
}
}
}
我对这段代码的问题是:GC 中的底层机制是什么,可确保它在最后一次对 SomeClass
实例的引用据称设置为 null 后继续跟踪它?
更准确地说...:
假设本地 s
存储了一个指向 SomeClass 实例的 _s
成员的直接指针(从下面的 windbg 反汇编),其最后的 "explicit" 引用在下一行 (x = null
),GC 如何跟踪 SomeClass
实例的 live-root 以防止此程序崩溃...?
Windbg 反汇编:
007ff9`e01504dc e8affbffff call 00007ff9`e0150090 (refreturn.SomeClass.get_S(), mdToken: 0000000006000001)
//rbp-30h stores the pointer to the struct
00007ff9`e01504e1 488945d0 mov qword ptr [rbp-30h],rax
// Now it's copied to rcx
00007ff9`e01504e5 488b4dd0 mov rcx,qword ptr [rbp-30h]
// And now copied to rbp-20h
00007ff9`e01504e9 48894de0 mov qword ptr [rbp-20h],rcx
00007ff9`e01504ed 33c9 xor ecx,ecx
// The last reference is overwritten with null
00007ff9`e01504ef 48894de8 mov qword ptr [rbp-18h],rcx
// rbp-20h is copied to rcx again
00007ff9`e01504f3 488b4de0 mov rcx,qword ptr [rbp-20h]
// Isn't this a possible boom?!?!?
00007ff9`e01504f7 8b09 mov ecx,dword ptr [rcx]
00007ff9`e01504f9 81c19a020000 add ecx,29Ah
00007ff9`e01504ff e85c634c5d call mscorlib_ni+0xd56860 (00007ffa`3d616860) (System.Console.WriteLine(Int32), mdToken: 0000000006000b5b)
00007ff9`e0150504 90 nop
How does the .NET GC / runtime make sure that this would never result in an access violation or reading wild pointers after the SomeClass backing memory has been re-used for something else?
出于与 x
不会引用已被 GC 清理的对象完全相同的原因。您知道这一点是因为 GC 不允许清理可通过托管代码访问的对象。在您清除它之前,可以通过 x
访问该对象,也可以通过 s
访问该对象。 s
是对该对象的引用,GC 在确定是否允许收集该对象时需要对其进行处理。
// This will store a direct pointer to x.S
ref var s = ref x.S;
它存储一个指向堆变量的托管内部指针;指针存储到短期存储上的某个位置。短期存储是 GC 的根。
// And now the GC will be free to re-use this memory
x = null;
天哪,没有。 在 GC root 中有一个活的托管内部指针。
How does the .NET GC / runtime make sure that this would never result in an access violation or reading wild pointers after the SomeClass backing memory has been re-used for something else?
直到内部托管指针不再是 GC 的根之后才释放该内存。或者,换句话说:通过实施正确的垃圾收集器。
我不知道你在这里真正问的是什么问题。 GC 阻止了错误 因为这是它唯一的工作 并且它已正确实施。