11

我刚刚了解到,null在 C 和 C++ 中取消引用有时会产生未定义的结果。这对我来说非常有趣,就像所有奇怪的编程行为一样(我曾经有人告诉我,他们在合法的生产环境中调试了“损坏的 RAM - 程序没有按书面方式运行”)。因为我主要是一名 Java 开发人员,所以我想知道这是否也可以在该语言中发生?

JLS 没有具体说明null参考是如何实现的(3.10.74.115.8.1),所以我不太确定。但我在想,可以通过使用Unsafe API直接操作内存地址来实现。不幸的是,我对 JVM 的内部工作原理没有足够的了解,无法知道这是否可能。

如果可能,那么恶意程序也有可能这样做,这将引发一个有趣的安全问题

那么:Java 是否有可能在取消引用时有未定义的行为null,而不是简单地抛出一个NullPointerException?

4

4 回答 4

10

JLS 没有具体说明如何实现空引用,它指定了它的行为。换句话说,没有未指定的行为。如果您遇到 JLS 中未指定的行为,这是一个错误。

让我澄清一下:您可以使用本机代码来破坏某些结构以使 JVM 崩溃,但这与任何 Java 行为无关。但是在典型的 JVM 实现中,null行为的实现是你最不想打扰的事情。重要的是,如果您从本机代码覆盖任意内存,您会丢弃什么。

“未指定的行为”意味着规范本身允许结果行为存在差异。Java 并非如此。

于 2013-10-21T18:29:30.290 回答
5

您无法从null纯 Java 中获得未定义的行为(除非 JVM 中存在严重错误!)。JLS 指定任何显式或隐式取消引用 a 的尝试null都将导致 a NullPointerException。没有任何回旋余地允许与处理null.

但是,如果您的应用程序包含 ... 或使用 ...native方法,则其中一种方法可能会以null导致未定义行为的方式错误处理 a。您还可以使用Unsafe该类获得未定义的行为。但是这两种情况都意味着您没有使用Java。(当您离开Java 时,JLS 的保证不再适用!)

(可能发生不可预知的事情的一个领域是多线程。但即便如此,一可能的行为也已定义。例如,如果您没有充分同步状态共享,您可能会在字段中看到陈旧的值。但您赢了'看不到完全随机的值......或导致分段违规的错误地址。)


如果可能,那么恶意程序也有可能这样做,这将引发一个有趣的安全问题。

恶意程序几乎可以做任何事情。但处理此问题的正确方法是在沙箱中执行您不信任的代码(即可能是恶意代码)。典型的沙箱会禁止调用Unsafe或加载本机库……以及许多其他恶意程序可以利用的东西。

于 2013-10-21T18:40:49.897 回答
1

具有未定义行为的语言特性的概念正是 C 和 C++ 标准的编写者用来明确标准不需要任何特定行为的东西。这使 C 和 C++ 的各种实现者可以为实现所针对的特定硬件或操作系统做任何最有效或最方便的事情。这是因为 C 总是优先考虑性能而不是可移植性。但是 Java 有相反的优先级。它的早期口号是“一次编写,随处运行”。所以 Java 语言规范没有谈论未定义的行为,而是力求定义所有语言特性的行为。

您似乎认为在某些情况下使用空引用可能会以某种方式破坏内存。我认为您将 C/C++ 指针与 Java 引用混淆了。指针本质上是一个内存地址:通过将其转换为 avoid *并取消引用它,您可以不受限制地破坏内存内容。Java 引用不像内存地址,因为垃圾收集器必须能够自由地将对象移动到内存中的不同位置。Java 引用到内存地址的转换以前是只有 JVM 才能做的事情。它永远不可能是 Java 程序本身可以做的事情。由于这种翻译完全由 JVM 控制,因此 JVM 可以确保翻译始终有效,并且始终指向它应该指向的对象,而不是其他任何地方。

于 2013-10-23T07:34:52.167 回答
1

该行为在15.12.4.4 Locate Method to Invoke中定义:

否则,将调用实例方法并且存在目标引用。如果目标引用为 null,则此时抛出 NullPointerException。否则,目标引用被称为引用一个目标对象,并将用作被调用方法中关键字 this 的值。然后考虑调用模式的其他四种可能性。

取消引用 null 应该引发 NullPointerException。

于 2013-10-21T18:44:49.960 回答