C# 垃圾收集器如何找到唯一引用是内部指针的对象? [英] How does the C# garbage collector find objects whose only reference is an interior pointer?

查看:25
本文介绍了C# 垃圾收集器如何找到唯一引用是内部指针的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据我所知,在 C# 中,refout 参数是通过仅传递相关值的原始地址来传递的.该地址可能是指向数组中的元素或对象中的字段的内部指针.

In C#, ref and out params are, as far as I know, passed by passing only the raw address of the relevant value. That address may be an interior pointer to an element in an array or a field within an object.

如果发生垃圾回收,可能引用某个对象是通过这些内部指针之一,如:

If a garbage collection occurs, it's possible that the only reference to some object is through one of these interior pointers, as in:

using System;

public class Foo
{
    public int field;

    public static void Increment(ref int x) {
        System.GC.Collect();
        x = x + 1;
        Console.WriteLine(x);
    }

    public static void Main()
    {
        Increment(ref new Foo().field);
    }
}

在这种情况下,GC 需要找到对象的开头并将整个对象视为可达.它是如何做到的?它是否必须扫描整个堆以查找包含该指针的对象?这似乎很慢.

In that case, the GC needs to find the beginning of the object and treat the entire object as reachable. How does it do that? Does it have to scan the entire heap looking for the object that contains that pointer? That seems slow.

推荐答案

垃圾收集器将有一种快速的方法来从托管的内部指针中找到对象的开始.在扫描阶段,它显然可以将对象标记为引用".

The garbage collector will have a fast way to find the start of an object from a managed interior pointer. From there it can obviously mark the object as "referenced" when doing the sweeping phase.

没有 Microsoft 收集器的代码,但他们会使用类似于 Go 的跨度表的东西,该表可以快速查找不同的内存跨度",您可以键入指针的最高有效 X 位,具体取决于您选择的跨度有多大.从那里他们使用每个跨度包含 X 个相同大小的对象的事实来非常快速地找到您拥有的对象的标题.这几乎是一个 O(1) 操作.显然,Microsoft 堆会有所不同,因为它是按顺序分配的,不考虑对象大小,但它们将具有某种 O(1) 查找结构.

Don't have the code for the Microsoft collector but they would use something similar to Go's span table which has a fast lookup for different "spans" of memory which you can key on the most significant X bits of the pointer depending on how large you choose the spans to be. From there they use the fact that each span contains X number of objets of the same size to very quickly find the header of the one you have. It's pretty much an O(1) operation. Obviously the Microsoft heap will be different since it's allocated sequentially without regard for object size but they will have some sort of O(1) lookup structure.

https://github.com/puppeh/gcc-6502/blob/master/libgo/runtime/mgc0.c

// Otherwise consult span table to find beginning.
// (Manually inlined copy of MHeap_LookupMaybe.)
k = (uintptr)obj>>PageShift;
x = k;
x -= (uintptr)runtime_mheap.arena_start>>PageShift;
s = runtime_mheap.spans[x];
if(s == nil || k < s->start || (const byte*)obj >= s->limit || s->state != MSpanInUse)
    return false;
p = (byte*)((uintptr)s->start<<PageShift);
if(s->sizeclass == 0) {
    obj = p;
} else {
    uintptr size = s->elemsize;
    int32 i = ((const byte*)obj - p)/size;
    obj = p+i*size;
}

请注意,.NET 垃圾收集器是一个复制收集器,因此每当在垃圾收集周期中移动对象时,都需要更新托管/内部指针.GC 将根据 JIT 时已知的方法参数知道每个堆栈帧的堆栈内部指针在哪里.

Note that the .NET garbage collector is a copying collector so managed/interior pointers need to be updated whenever the object is moved during a garbage collection cycle. The GC will be aware of where in the stack interior pointers are for each stack frame based on the method parameters known at JIT time.

这篇关于C# 垃圾收集器如何找到唯一引用是内部指针的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆