首页 > 解决方案 > 从托管 'ref' 内部指针中恢复包含 GC 对象

问题描述

鉴于最新版本的C# 7中的新ref localsref return功能,这个问题是新的相关:

随着 C# 中托管或“内部”指针变量的日益突出和更广泛的使用,有时您可能需要为此类指针恢复相应的包含 PinnableGC 对象。例如,如果您将托管指针传递给类型为 的数组元素T,则可能需要数组引用T[]本身才能调用 (eg) Array.Copy(...)

因此,从托管代码中,考虑到以下流行的内部/托管指针 ( , , ) 的使用,是否有任何合理合法的方法来恢复包含的 GC 对象句柄:refoutin

  1. 指向GC 对象实例中(structclass字段的内部指针;
  2. 指向Array的(structclass元素 的托管指针。TT[]

在.NET内部,GC 似乎使用以下函数:/coreclr/master/src/gc/gc.cpp

#ifdef INTERIOR_POINTERS
// will find all heap objects (large and small)
uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low)
{
    ....

此代码遍历已知的 GC 堆,检查指定的内部指针是否落在已知 GC 对象分配的范围内。显然,这种方法不容易从最终用户管理的代码中访问,而且据我所知,如果没有正在进行的 GC,它甚至可能不相关。

我还查看了 newSpan<T>System.Memory库,但是如果您没有首先提供数组句柄,则找不到可以恢复(例如)数组句柄的操作序列(因此包含句柄在那些各种结构)。如果_pinnable是可选的(例如Span<T>),则结构中的 GC 句柄是null,因此如果您不从一开始就选择加入,则无法将其取回。

摘要:有没有办法从托管指针中恢复包含对象的句柄?

[编辑:] 如果托管指针指向堆栈上的值类型,那么(假定的)句柄恢复函数通过(例如)返回“null”来指示失败是完全合理的。


相关:C# 垃圾收集器如何找到唯一引用是内部指针的对象?

标签: .netgarbage-collectionmanagedref

解决方案


不,不可能从内部指针恢复包含对象。在 GC 期间,由于所谓的砖表插头树,内部指针被转换为相应的对象。给定一个指定的地址,计算正确的砖表条目并遍历相应的插头树以找到该地址所在的插头。最后,逐个对象扫描该插件以找到包含所考虑地址的插件。

关键是这些树仅在 GC 期间构建并可用。因此,即使存在这样的“内部指针恢复”API,它也必须等待 GC,并且只能在之后提供答案(这似乎非常不切实际)。其他解决方案,如线性内存扫描,显然可能会引入巨大的开销。


推荐阅读