GC 可以移动引用;使用 unsafe 使对象不受 GC 控制,并避免了这种情况。 “固定”固定对象,但让 GC 管理内存。
根据定义,如果你有一个指向对象地址的指针,而 GC 移动了它,你的指针就不再有效。
关于为什么需要指针:主要原因是使用非托管 DLL,例如那些用 C++ 编写的
另请注意,当您固定变量并使用指针时,您更容易受到堆碎片的影响。
编辑
您已经谈到了托管代码与非托管代码的核心问题......内存是如何释放的?
您可以按照您的描述混合代码以提高性能,只是不能使用指针跨越托管/非托管边界(即,您不能在“不安全”上下文之外使用指针)。
至于它们是如何被清理的……你必须管理自己的记忆;您的指针指向的对象是使用(希望)CoTaskMemAlloc() 创建/分配的(通常在 C++ DLL 中),并且您必须以相同的方式释放该内存,调用 CoTaskMemFree(),否则您将有内存泄漏.请注意,只有使用CoTaskMemAlloc() 分配的内存才能使用CoTaskMemFree() 释放。
另一种选择是从您的本机 C++ dll 中公开一个方法,该方法接受一个指针并释放它...这让 DLL 决定如何释放内存,如果它使用其他方法来分配内存,则效果最好。您使用的大多数本机 dll 都是您无法修改的第三方 dll,而且它们通常没有(我见过的)此类可调用的函数。
释放内存的例子,取自here:
string[] array = new string[2];
array[0] = "hello";
array[1] = "world";
IntPtr ptr = test(array);
string result = Marshal.PtrToStringAuto(ptr);
Marshal.FreeCoTaskMem(ptr);
System.Console.WriteLine(result);
更多阅读材料:
C# deallocate memory referenced by IntPtr
下面的第二个答案解释了不同的分配/解除分配方法
How to free IntPtr in C#?
加强了以与分配内存相同的方式解除分配的需要
http://msdn.microsoft.com/en-us/library/aa366533%28VS.85%29.aspx
有关分配和释放内存的各种方法的官方 MSDN 文档。
简而言之...您需要知道内存是如何分配的才能释放它。
编辑
如果我正确理解您的问题,简短的回答是肯定的,您可以将数据交给非托管指针,在不安全的上下文中使用它,并在退出不安全的上下文后使数据可用。
关键是您必须使用fixed 块固定您引用的托管对象。这可以防止您引用的内存在 unsafe 块中被 GC 移动。这里涉及许多微妙之处,例如您不能重新分配在固定块中初始化的指针...如果您真的打算管理自己的代码,则应该阅读不安全和固定的语句。
总而言之,管理您自己的对象和以您描述的方式使用指针的好处可能不会像您想象的那样为您带来性能提升。为什么不这样做的原因:
- C# 非常优化且速度非常快
- 您的指针代码仍以 IL 形式生成,必须对其进行 jitted(此时需要进一步优化)
- 您并没有关闭垃圾收集器...您只是将您正在使用的对象排除在 GC 的权限之外。因此,每隔 100 毫秒左右,GC仍然会中断您的代码并为托管代码中的所有其他变量执行其函数。
HTH,
詹姆斯