gpt4 book ai didi

c# - 引发/生成空引用异常背后的CLR实现是什么?

转载 作者:太空狗 更新时间:2023-10-29 22:14:23 29 4
gpt4 key购买 nike

在编码/开发生命的一天或另一天,我们确实遇到了这个特殊的情况,这是最常见的异常(exception)之一。我的问题是关于而不是。为什么(我知道当我们尝试访问实际上指向null的引用变量的属性时会引发此问题),但它涉及的是 HOW ,CLR会生成NULL REFERENCE EXCEPTION。

有时我不得不考虑一种机制,该机制用于标识对null的引用(也许null是内存中的保留空间),然后由CLR引发Exception。 CLR如何识别并引发此特殊异常。操作系统在其中扮演任何角色吗?

我想分享一个关于它的最有趣的说法:

null is actually an all time reserved memory space known to CLR, and all kind of access are prohibited. Thus , when reference for that space is found, it by default generates access denied kind of exception via OS which is interpreted as a NULL Reference Exception by CLR.



我没有发现任何文章或帖子支持上述声明,因此难以置信。可能由于缺少详细信息或其他原因,我希望Stackoverflow是最合适的平台之一,我将在该平台上获得最佳响应。

最佳答案

它不是必须的(可以进行显式检查),但是它可以通过捕获访问冲突异常来工作。

.NET对象将变成 native 对象:其字段变成以特定方式布置的内存块,其方法被绑定(bind)到 native 代码方法中,并创建v表或其他虚拟方法重载机制。

  • 然后访问一个字段,意味着找到对象的地址,加上成员的偏移量,并读取或写入所引用的内存。
  • 调用虚拟方法,意味着找到对象的地址,找到其方法表(在对象内设置偏移量),找到方法的地址(在表内设置偏移量),然后在该地址处调用方法,对象的地址为通过(this指针)。
  • 调用非虚拟方法,意味着使用传递的对象的地址(this指针)调用该方法。

  • 显然,如果问题处的地址上没有实际的对象,则情况1和2会以某种方式出错,而情况3会起作用(但又可能导致情况1或2)。有两种主要方法可以解决此问题:
  • 它可以访问实际上不是我们类型的对象的任意内存,从而导致各种令人兴奋的问题,并且很难跟踪错误(.NET代码通常不会导致这种情况)。
  • 它可以访问 protected 任意内存,从而导致访问冲突。

  • 您可能从C,C++或ASM编码中了解第二种情况。如果没有,您可能仍然会看到程序崩溃,并且以垂死的呼吸谈论某个地址的访问冲突。如果是这样,您可能已经注意到,虽然给定的地址可能几乎是任何东西,但它通常是0x00000000或非常低的值,例如0x00000020。这些是由于代码尝试取消引用空指针以访问字段还是调用虚拟方法(本质上是访问字段然后根据获取的内容进行调用)而导致的。

    现在,由于第一个64k或内存始终受到保护,因此取消引用空指针将始终导致第二种情况(访问冲突),而不是第一种情况(滥用任意内存并导致异常的“fandango on the core”错误) )。

    这与.NET完全相同(或更确切地说,由它生成的代码),但如果(A)访问冲突发生在地址低于0x00010000的情况下,并且(B)发现此类冲突发生于被伪装的代码,然后将其转换为 NullReferenceException,否则将其转换为 AccessViolationException

    我们可以使用不会取消引用但可以访问 protected 内存的代码来模拟它(我们只会读取,因此,如果我们偶然偶然碰到了未 protected 内存,结果将不会太奇怪!) :

    以下代码将引发AccessViolationException:
    unsafe
    {
    int read = *((int*)long.MaxValue - 8);
    }

    以下代码将引发NullReferenceException:
    unsafe
    {
    int read = *((int*)8);
    }

    这两个代码实际上都没有取消引用任何东西。两者都导致访问冲突,但是CLR认为后者可能是由空引用(公平地说,到目前为止,最有可能的情况)引起的,并引发了该冲突。

    因此,我们可以看到字段访问和 callvirt是如何导致这种情况的。

    现在值得注意的是,由于即使在安全的情况下也决定不允许C#调用空引用上的方法,因此 callvirt用作C#中大多数情况的IL,唯一的异常(exception)是静态方法的情况或在编译时显示为不在null引用上的位置。 (编辑:在其他一些情况下,编译器可以看到 callvirt可以被 call代替,即使该方法实际上是虚拟的[如果编译器可以判断将发生重载的情况下],以后的编译器也可以做到这一点频率稍高一些,尽管它仍会比您想象的更频繁地使用 callvirt)。

    一个有趣的情况是优化意味着可以内联使用 callvirt调用的方法,但是在编译时不知道该方法是否可以保证为非空。在这种情况下,可以在“调用”(实际上不是调用)发生的地方之前添加字段访问,以精确地在方法的开始而不是在中间触发 NullReferenceException。这意味着优化不会改变观察到的行为。

    关于c# - 引发/生成空引用异常背后的CLR实现是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11239410/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com