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

本文关键字:异常 其他 任何 NullReferenceException CLR 为什么 | 更新日期: 2023-09-27 18:08:08

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

捕获空解引用是CLR可以执行的最昂贵的操作之一,如果您的代码抛出大量nullreferenceexception,这可能会对性能产生严重影响。测试null并对它做一些其他的事情(甚至抛出一个异常!)是一个更便宜的操作。

这是我找到的问题的链接。什么时候处理空指针/引用异常优先于做空检查?

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

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

这里的重点不是NullReferenceException比任何其他异常更昂贵。抛出异常的代价来自展开堆栈,这更多地取决于调用结构,而不是特定的异常。我相信文档想要表达的观点是,首先防范异常的成本要低于在异常抛出时处理异常的成本。

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

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

很难给出性能差异的具体值,因为有太多的变量在起作用。在最坏的情况下,我可以想象它可能会慢一个数量级——例如,能够在处理一个硬件异常所需的时间内抛出和捕获大约100个本地软件异常。在实践中,尽管我猜测性能比率在3:1到10:1之间,这取决于CPU,操作系统,c# CLR和共享堆/GC的活动线程的数量。