c# - ref 如何/为什么返回实例成员

标签 c# garbage-collection return jit ref

我试图理解为什么/如何在将 ref 返回给类成员的情况下进行 ref-return。 换句话说,我想从 CLR 的内存安全方面了解保证实例成员的引用返回有效的运行时的内部工作原理。

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."

这是一个代码片段,可以干净地编译和运行并演示返回一个实例字段作为 ref 返回:

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 中的底层机制是什么,以确保它在最后一次对它的引用被假定为 null 之后继续跟踪 SomeClass 实例? 更准确地说……:

鉴于本地 s 存储了一个直接指向 SomeClass 实例的 _s 成员的指针(从下面的 windbg 反汇编),其最后一个“显式”引用被 null 覆盖在下一行 (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

最佳答案

 // 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 根中有一个活的托管内部指针

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 可以防止错误因为这是它唯一的工作并且它已正确实现。

关于c# - ref 如何/为什么返回实例成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47914312/

相关文章:

Java枚举函数返回值

c# - Selenium - C# - Webdriver - 无法找到元素

c# - 打破 ASP.NET 中文件背后的大型代码

java - 返回不兼容的类型 (java)

c++ - 理解这种递归的困难

java - 垃圾收集和反射

c# - 从同一进程托管和使用 WCF 服务

c# - 设置事件 Chrome 窗口 (C++)

Android: GC_FOR_MALLOC 由长时间的触摸事件引起?

c# - 第三代对象和大对象堆之间的区别