tri*_*kbz 5 c# garbage-collection
阅读Richter J书的“手动监视和控制对象的生存期”部分。Jeffrey说,有两种方法可以使用GCHandle类控制对象的生存期:
他说,两种方式都可以用来将托管对象传递给非托管代码。他试图解释,开发人员何时应使用GCHandleType.Normal标志调用Alloc。我不太了解有关正常标志用法的解释。在这两种方式中,我们都不允许GC收集对象,这些对象在GC描述符表中具有此类标志,但是在“固定”的情况下,我们还防止此类对象在垃圾收集期间移动。据我了解,在普通模式下,不是直接引用(内存地址)传递给非托管代码,而是传递给GC描述符表的索引。而且,当非托管代码被调用回托管代码时,该索引将被转换为当前/实际地址。我一头雾水,谷歌和微软几乎没有详细信息,只有复制粘贴。
我的问题:
从GCHandle的文档中:
提供一种从非托管内存访问托管对象的方法。
如果您打算从非托管代码访问对象,则需要固定该对象。为了获得固定对象,必须可以将其编组到非托管内存。
如果你只需要一个不透明的句柄传递给非托管代码,使得非托管代码可以再次通过它,而无需访问它,那么你并不需要一个固定的对象,但你仍然需要确保它不被删除垃圾收集器。
考虑这个类:
public class MyClass
{
DateTime dt = DateTime.Now;
}
Run Code Online (Sandbox Code Playgroud)
如果您尝试像这样获得固定句柄:
MyClass o = new MyClass();
GCHandle h = GCHandle.Alloc(o, GCHandleType.Pinned);
Run Code Online (Sandbox Code Playgroud)
您将收到以下消息的异常:
对象包含非原始或非 blittable 数据。
这是因为返回的句柄允许您获取固定对象的地址。为了在非托管代码中使用该地址,必须将对象从托管内存编组到非托管内存。
此代码不会引发异常:
MyClass o = new MyClass();
GCHandle h = GCHandle.Alloc(o, GCHandleType.Normal);
Run Code Online (Sandbox Code Playgroud)
因为不能使用返回的句柄来获取地址。
所以回答你的问题:
一种方案是用于托管程序集的纯 C API。API 可能如下所示:
MYHANDLE h1 = MyLib_CreateComponent();
MYHANDLE h2 = MyLib_CreateComponent();
MyLib_SetX(h1, 9.81);
double y1 = MYLib_CalcY(h1);
MyLib_SetX(h2, 3.14);
double y2 = MyLib_CalcY(h2);
printf("z = %f\n", y1 + y2);
MyLib_DestroyComponent(h1);
MyLib_DestroyComponent(h2);
Run Code Online (Sandbox Code Playgroud)
不能从 C 代码直接访问对象。
MyLib_CreateComponent() 函数的 C# 实现如下所示:
public static int CreateComponent()
{
MyClass instance = new MyClass();
GCHandle gch = GCHandle.Alloc(instance, GCHandleType.Normal);
IntPtr ip = GCHandle.ToIntPtr(h);
h = ip.ToInt32();
return h;
}
Run Code Online (Sandbox Code Playgroud)
在托管代码中,我将创建一个使用句柄获取对象的方法:
static MyClass GetObjectFromHandle(int hComp)
{
IntPtr ip = new IntPtr(hComp);
GCHandle h = GCHandle.FromIntPtr(ip);
MyClass comp = h.Target as MyClass;
return comp;
}
Run Code Online (Sandbox Code Playgroud)
如果在使用 创建句柄之前将对象引用传递给本机代码不安全GCHandleType.Normal,那么在创建此类句柄之后也不安全,因为非托管代码需要稳定的指针。因此,句柄 withGCHandleType.Normal对非托管代码没有任何作用。我认为否则提出建议是一个文档错误。
GCHandleType.Normal被托管代码用来创建不会消亡的对象。例如,某些Timer类使它们自己的实例保持活动状态,以便当您删除对它的最后一个引用时计时器不会停止。
据我了解,在正常模式下,不会将直接引用(内存地址)传递给非托管代码,而只是传递 GC 描述符表中的索引。
这不可能是真的,因为在 PInvoke 发生时,没有足够的信息来判断 a 是否GCHandle与您想要传递的对象关联。即使粉碎者想要这样做,也做不到。另外,非托管代码会对句柄表条目做什么?它不明白。句柄表是 CLR 内部的。
某些应用程序根(非弱)引用托管堆中的对象,并且不再有根。这是否意味着 GC 描述符表中的相应条目将带有 GCHandleType.Normal 标志?看起来不行,因为 Jeffrey 说,“即使应用程序代码中可能没有引用,GC 也无法删除对象”。但如果不是,该表条目将具有哪个标志?
它具有您在创建该文件时传入的标志GCHandle。该表中只有带有GCHandle. 不跟踪普通对象。
| 归档时间: |
|
| 查看次数: |
1214 次 |
| 最近记录: |