C#内存管理:不安全的关键字和指针

时间:2010-03-22 22:30:58

标签: c# memory-management pointers keyword unsafe

C#中使用 unsafe 关键字来使用指针会产生什么后果(正面/负面)?例如,什么是垃圾收集,性能增益/损失是什么,与其他语言手动内存管理相比,性能增益/损失是什么,危险是什么,在哪种情况下使用这种语言是合理的功能,编译时间更长......?

4 个答案:

答案 0 :(得分:27)

正如Conrad已经提到的,在某些情况下,C#中对内存的不安全访问很有用。它们中没有那么多,但有一些:

  • 使用Bitmap 进行操作几乎是一个典型示例,您需要使用unsafe获得一些额外的性能。

  • 互操作性与较旧的API(例如WinAPI或本机C / C ++ DLL)是unsafe非常有用的另一个领域 - 例如,您可能想要调用接受/返回非托管指针的函数。

另一方面,您可以使用Marshall class编写大部分内容,这会隐藏方法调用中的许多不安全操作。这会有点慢,但是如果你想避免使用unsafe(或者如果你使用的是没有unsafe的VB.NET),这是一个选项。

积极的后果: 因此,在{C#中存在unsafe的主要积极结果是,您可以更轻松地编写一些代码(互操作性),并且可以更有效地编写一些代码(使用位图进行操作或使用数组进行一些繁重的数值计算 - 虽然,我对第二个不太确定。)

负面后果:当然,使用unsafe需要付出一些代价:

  • 不可验证的代码:使用unsafe功能编写的C#代码变得不可验证,这意味着您的代码可能以任何方式危及运行时。这不是完全信任方案中的大问题(例如,不受限制的桌面应用程序) - 您只是没有所有漂亮的.NET CLR保证。但是,您无法在受限制的环境中运行应用程序,例如公共Web托管,Silverlight或部分信任(例如,从网络运行的应用程序)。

  • 垃圾收集器在使用unsafe时也需要小心。通常允许GC重新定位托管堆上的对象(以保持内存碎片整理)。当你指向某个对象时,你需要使用fixed关键字告诉GC它不能移动对象直到你完成(这可能会影响垃圾收集的性能 - 但当然,取决于确切的情况)。

我的猜测是,如果C#不必与旧代码进行互操作,它可能不会支持unsafe(而像Singularity这样的研究项目试图创建基于托管语言的更可验证的操作系统肯定不允许使用usnsafe码)。但是,在现实世界中,unsafe在某些(罕见)情况下很有用。

答案 1 :(得分:9)

我可以给你一个值得使用的情况:

我必须逐个像素地生成位图。 Drawing.Bitmap.SetPixel()太慢了。因此,我构建了自己的托管Array位图数据,并使用unsafe获取Bitmap.Bitmap( Int32, Int32, Int32, PixelFormat, IntPtr)IntPtr

答案 2 :(得分:5)

引用专业C#2008:

  

“使用的两个主要原因   指针是:

     
      
  1. 向后兼容性 - 尽管提供了所有设施   .NET运行时仍然可以   调用本机Windows API函数,和   对于某些操作,这可能是   唯一的方法来完成你的任务。   这些API函数通常是   用C语写,经常需要   指针作为参数。但是,在   很多情况下都可以编写   DllImport声明的方式   避免使用指针;例如,   通过使用System.IntPtr类。
  2.   
  3. 表现 - 在速度最快的场合   重要性,指针可以提供   优化性能的途径。如果你   知道你在做什么,你可以   确保访问数据或   以最有效的方式操纵。   但是,请注意,更多时候   而不是,还有其他领域   你需要的代码   性能改进没有   转向指针。尝试使用   代码分析器,以寻找瓶颈   在你的代码中 - 一个附带Visual   Studio 2008。“
  4.   

如果您使用指针,您的代码将需要更高的信任杠杆才能执行,如果用户不允许您的代码无法运行。

用最后一句话把它包起来:

  

“我们强烈建议不要使用   指针不必要因为它会   不仅难以编写和调试,   但它也会失败记忆   由...强制进行的类型安全检查   CLR“。

答案 3 :(得分:2)

对于长寿命对象,垃圾收集效率低下。当大多数对象快速释放时,.Net的垃圾收集器效果最佳,而某些对象“永远存在”。问题是较长寿命的对象仅在完全垃圾收集期间释放,这会导致显着的性能损失。从本质上讲,长寿命物体很快就会进入第二代。

(有关更多信息,您可能需要阅读.Net的分代垃圾收集器:http://msdn.microsoft.com/en-us/library/ms973837.aspx

在一般情况下对象或内存使用寿命很长的情况下,手动内存管理会产生更好的性能,因为它可以在不需要完整垃圾回收的情况下发布到系统。

基于单个大字节数组,结构和大量指针算法实现某种内存管理系统,理论上可以在数据长时间存储在RAM中的情况下提高性能。

不幸的是,我不知道在.Net中为长期存在的对象进行手动内存管理的好方法。这基本上意味着在RAM中具有长寿命数据的应用程序在运行所有内存的完整垃圾收集时会定期无响应。