在C#中使用unsafe关键字来使用指针有什么后果(正面/负面) ?例如,垃圾收集会变成什么,性能增益/损失是什么,与其他语言手动内存管理相比,性能增益/损失是什么,有什么危险,在什么情况下使用这种语言是真正合理的功能,编译时间更长吗...?
4 回答
正如 Conrad 已经提到的,在某些情况下,在 C# 中不安全地访问内存很有用。它们的数量不多,但有一些:
使用 with
Bitmap
进行操作几乎是一个典型的例子,你需要一些额外的性能,你可以通过使用unsafe
.与旧 API(如 WinAPI 或本机 C/C++ DLL)的互操作性
unsafe
是另一个非常有用的领域 - 例如,您可能想要调用一个接受/返回非托管指针的函数。
另一方面,您可以使用Marshall
class编写大部分内容,这将许多不安全的操作隐藏在方法调用中。这会慢一点,但如果您想避免使用unsafe
(或者如果您使用的 VB.NET 没有unsafe
) ,这是一个选项
积极的后果:因此,在 C# 中存在的主要积极后果unsafe
是您可以更轻松地编写一些代码(互操作性)并且您可以更有效地编写一些代码(使用位图或可能使用数组进行一些繁重的数字计算 - 虽然,我不太确定第二个)。
负面后果:当然,您必须为使用付出一些代价unsafe
:
不可验证的代码:使用这些
unsafe
功能编写的 C# 代码变得不可验证,这意味着您的代码可能会以任何方式损害运行时。在完全信任的场景(例如不受限制的桌面应用程序)中,这不是一个大问题——您只是没有所有好的 .NET CLR 保证。但是,您不能在受限环境中运行应用程序,例如公共网络托管、Silverlight 或部分信任(例如从网络运行的应用程序)。垃圾收集器在使用时也需要小心
unsafe
。通常允许 GC 重新定位托管堆上的对象(以保持内存碎片整理)。当您使用指向某个对象的指针时,您需要使用fixed
关键字告诉 GC 在您完成之前它不能移动对象(这可能会影响垃圾收集的性能 - 当然,这取决于确切的场景)。
我的猜测是,如果 C# 不必与旧代码互操作,它可能不会支持unsafe
(并且像 Singularity 这样的研究项目试图基于托管语言创建更可验证的操作系统肯定不允许使用 usnsafe 代码)。但是,在现实世界中,unsafe
在某些(罕见)情况下很有用。
我可以给你一个值得使用的情况:
我必须逐像素生成位图。Drawing.Bitmap.SetPixel()
太慢了。所以我建立了我自己管理Array
的位图数据,并unsafe
用来获取IntPtr
for Bitmap.Bitmap( Int32, Int32, Int32, PixelFormat, IntPtr)
.
引用 Professional C# 2008:
“使用指针的两个主要原因是:
- 向后兼容性——尽管 .NET 运行时提供了所有功能,但仍然可以调用本机 Windows API 函数,并且对于某些操作,这可能是完成任务的唯一方法。这些 API 函数通常是用 C 语言编写的,并且通常需要指针作为参数。但是,在许多情况下,可以通过避免使用指针的方式编写 DllImport 声明;例如,通过使用 System.IntPtr 类。
- 性能- 在速度至关重要的情况下,指针可以提供优化性能的途径。如果您知道自己在做什么,则可以确保以最有效的方式访问或操作数据。但是,请注意,通常情况下,您的代码的其他区域可以在不依赖指针的情况下进行必要的性能改进。尝试使用代码分析器来查找代码中的瓶颈——Visual Studio 2008 附带了一个。”
如果您使用指针,您的代码将需要更高的信任级别才能执行,并且如果用户不同意您的代码将无法运行。
并用最后一句话总结:
“我们强烈建议不要不必要地使用指针,因为它不仅会更难编写和调试,而且还会使 CLR 强加的内存类型安全检查失败。”
Garbage Collection is inefficient with long-lived objects. .Net's garbage collector works best when most objects are released rather quickly, and some objects "live forever." The problem is that longer-living objects are only released during full garbage collections, which incurs a significant performance penalty. In essence, long-living objects quickly move into generation 2.
(For more information, you might want to read up on .Net's generational garbage collector: http://msdn.microsoft.com/en-us/library/ms973837.aspx)
In situations where objects, or memory use in general, is going to be long-lived, manual memory management will yield better performance because it can be released to the system without requiring a full garbage collection.
Implementing some kind of a memory management system based around a single large byte array, structs, and lots of pointer arithmetic, could theoretically increase performance in situations where data will be stored in RAM for a long time.
Unfortunately, I'm not aware of a good way to do manual memory management in .Net for objects that are going to be long-lived. This basically means that applications that have long-lived data in RAM will periodically become unresponsive when they run a full garbage collection of all of the memory.