为什么 NullReferenceException 比 CLR 中的任何其他异常都昂贵?

How come NullReferenceException is more expensive than any other exception in CLR?

在查找 null 检查与捕获 NullReferenceException 时,我遇到了这个。

Catching a null dereference is one of the most expensive operations that the CLR can perform, and this could have a severe performance impact if your code is throwing a lot of NullReferenceExceptions. Testing for nullity and doing something else about it (even throwing an exception!) is a cheaper operation.

Here is a link of the question where I found it. When is handling a null pointer/reference exception preferred over doing a null check?

我想知道捕获 NullReferenceException 怎么会比 null 检查和抛出异常更昂贵。

[由于信誉数不足,无法在那里发表评论]

这里的重点不是 NullReferenceException 比任何其他异常都昂贵。抛出异常的成本来自展开堆栈,这更多地取决于调用结构而不是特定的异常。我相信该文档试图说明的观点是,与抛出异常时进行处理相比,首先防范异常的成本更低。

A NullReferenceException 是一个 硬件异常 ,它首先由 Windows 页面错误处理程序处理.此操作需要一个 kernel context switch,这可能是一个非常昂贵的操作,具体取决于底层 CPU 硬件以及操作系统安全模型。此外,垃圾收集器通常必须暂停某些操作,直到上下文切换完成。由于 GC 为所有线程和应用程序域提供服务,因此 GC 暂停的任何时间都可能导致潜在的重大性能影响,这种影响只会在意外间隔出现。

相比之下,通过 throw 关键字从 C# 中抛出您自己的异常会生成 软件异常。这些不需要特殊处理,并且在异常的 catch 处理程序是本地的(在同一函数内)的情况下,可以进一步优化,因为不需要额外的展开来发现适当的 catch 处理程序。这就是为什么 MSDN 文档建议不要依赖 NullReferenceException.

很难给出性能差异的具体值,因为有太多变数在起作用。在最坏的情况下,我可以想象它可能会慢一个数量级——例如,能够在处理单个硬件异常所花费的时间内抛出并捕获大约一百个本地软件异常。在实践中,虽然我猜测性能比在 3:1 到 10:1 附近,具体取决于 CPU、OS、C# CLR 和共享Heap/GC.