Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 压缩发生后,GC如何更新引用_C#_.net_Garbage Collection_Clr - Fatal编程技术网

C# 压缩发生后,GC如何更新引用

C# 压缩发生后,GC如何更新引用,c#,.net,garbage-collection,clr,C#,.net,Garbage Collection,Clr,NET垃圾收集器收集对象(回收它们的内存)并执行内存压缩(以将内存碎片保持在最低限度) 我想知道,由于应用程序可能有许多对对象的引用,当对象的地址由于GC进行压缩而发生更改时,GC(或CLR)如何管理这些对对象的引用。垃圾收集 每个应用程序都有一组根。根标识存储位置,这些位置引用托管堆上的对象或设置为null的对象。例如,应用程序中的所有全局和静态对象指针都被视为应用程序根的一部分。此外,线程堆栈上的任何局部变量/参数对象指针都被视为应用程序根的一部分。最后,任何包含指向托管堆中对象指针的CPU

NET垃圾收集器收集对象(回收它们的内存)并执行内存压缩(以将内存碎片保持在最低限度)

我想知道,由于应用程序可能有许多对对象的引用,当对象的地址由于GC进行压缩而发生更改时,GC(或CLR)如何管理这些对对象的引用。

垃圾收集 每个应用程序都有一组根。根标识存储位置,这些位置引用托管堆上的对象或设置为null的对象。例如,应用程序中的所有全局和静态对象指针都被视为应用程序根的一部分。此外,线程堆栈上的任何局部变量/参数对象指针都被视为应用程序根的一部分。最后,任何包含指向托管堆中对象指针的CPU寄存器也被视为应用程序根的一部分。活动根的列表由即时(JIT)编译器和公共语言运行库维护,并可供垃圾收集器的算法访问

当垃圾收集器开始运行时,它假设堆中的所有对象都是垃圾。换句话说,它假设应用程序的根都没有引用堆中的任何对象。现在,垃圾收集器开始遍历根,并构建一个所有可从根访问的对象的图形。例如,垃圾收集器可以定位指向堆中对象的全局变量

一旦图形的这一部分完成,垃圾收集器将检查下一个根并再次遍历对象。当垃圾收集器从一个对象移动到另一个对象时,如果它试图将一个对象添加到它先前添加的图形中,则垃圾收集器可以停止沿着该路径移动。这有两个目的。首先,它可以显著提高性能,因为它不会多次遍历一组对象。其次,如果您有任何对象的循环链接列表,它可以防止无限循环

检查完所有根之后,垃圾收集器的图包含了所有可以从应用程序根访问的对象集;任何不在图中的对象都不能被应用程序访问,因此被视为垃圾垃圾收集器现在线性地遍历堆,寻找连续的垃圾对象块(现在被认为是可用空间)。然后,垃圾收集器在内存中向下移动非垃圾对象(使用您已经知道多年的标准memcpy函数),消除堆中的所有间隙。当然,在内存中移动对象会使指向对象的所有指针失效。因此,垃圾收集器必须修改应用程序的根,以便指针指向对象的新位置。此外,如果任何对象包含指向另一个对象的指针,垃圾收集器也负责更正这些指针。

C#固定语句 fixed语句设置一个指向托管变量的指针,并在语句执行期间“固定”该变量。如果没有固定的,指向可移动托管变量的指针将没有什么用处,因为垃圾收集可能会不可预测地重新定位变量。C#编译器只允许在固定语句中为托管变量分配指针


这个概念非常简单,垃圾收集器只需更新任何对象引用并将它们重新指向移动的对象

实现有点棘手,本机代码和托管代码之间并没有真正的区别,它们都是机器代码。对象引用没有什么特别之处,它只是运行时的指针。收集器需要一种可靠的方法来找到这些指针,并将它们识别为引用托管对象的指针。不仅要在压缩时移动指向的对象时更新它们,还要识别活动引用,以确保不会过早收集对象

这对于存储在GC堆上的类对象中的任何对象引用都很简单,CLR知道对象的布局以及存储指针的字段。对于存储在堆栈或cpu寄存器中的对象引用来说,这并不是那么简单。比如局部变量和方法参数

执行托管代码区别于本机代码的关键特性是,CLR可以可靠地迭代托管代码所拥有的堆栈帧。通过限制用于设置堆栈帧的代码类型来完成。这在本机代码中通常是不可能的,“帧指针省略”优化选项尤其令人讨厌

堆栈帧遍历首先允许它查找存储在堆栈上的对象引用。并让它知道线程当前正在执行托管代码,因此也应该检查cpu寄存器的引用。从托管代码到本机代码的转换涉及在堆栈上写入收集器识别的特殊“cookie”。因此,它知道不应该检查任何后续堆栈帧,因为它们将包含从未引用托管对象的随机指针值


启用非托管代码调试时,可以在调试器中看到这一点。查看调用堆栈窗口,注意[Native to Managed Transition]和[Managed to Native Transition]注释。这是调试器识别那些cookie。这对它也很重要,因为它需要知道“局部变量”窗口是否可以显示任何有意义的内容。堆栈漫游也在框架中公开,请注意StackTrace和StackFrame类。这对于沙箱非常重要,代码访问安全性(CAS)执行堆栈遍历。

为了简单起见,我将假设一个停止世界GC,其中没有对象被固定,每个对象在每个G上都被扫描和重新定位