Objects.requireNonNull 是否不如旧方法有效?

2022-08-31 14:58:28

从JDK 7开始,我一直很高兴使用它引入的方法来拒绝传递给无法接受它们的方法的值:null

private void someMethod(SomeType pointer, SomeType anotherPointer) {
    Objects.requireNonNull(pointer, "pointer cannot be null!");
    Objects.requireNonNull(anotherPointer, "anotherPointer cannot be null!");
    // Rest of method
}

我认为这种方法可以使代码非常整洁,易于阅读,我试图鼓励同事使用它。但一位(特别是知识渊博的)同事持抵制态度,他说旧方法更有效:

private void someMethod(SomeType pointer, SomeType anotherPointer) {
    if (pointer == null) {
        throw new NullPointerException("pointer cannot be null!");
    }
    if (anotherPointer == null) {
        throw new NullPointerException("anotherPointer cannot be null!");
    }
    // Rest of method
}

他说,调用涉及在JVM调用堆栈上放置另一个方法,并且会导致比简单检查更差的性能。requireNonNull== null

所以我的问题是:是否有任何证据表明使用这些方法会产生性能损失?Objects.requireNonNull


答案 1

让我们看一下 Oracle 的 JDK 中的 实现:requireNonNull

public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

很简单。JVM(无论如何是Oracle的)包括一个优化的两阶段即时编译器,用于将字节码转换为机器代码。如果这样可以获得更好的性能,它将内联像这样的琐碎方法。

所以不,不太可能变慢,不以任何有意义的方式,不在任何重要的地方。

所以我的问题是:是否有任何证据表明使用这些方法会产生性能损失?Objects.requireNonNull

唯一重要的证据是对代码库的性能测量,或者对设计为高度代表它的代码的性能测量。你可以用任何像样的性能测试工具来测试它,但除非你的同事能指出一个与这种方法相关的代码库中性能问题的真实例子(而不是一个合成的基准),否则我倾向于假设你和他/她有更大的鱼要炸。


顺便说一句,我注意到您的示例方法是一种方法。因此,只有您的团队直接编写代码调用它。在这些情况下,您可以查看是否有用于断言而不是运行时检查的用例。断言的优点是根本不在“已发布”代码中执行,因此比问题中的任何一种替代方案都快。显然,有些地方需要运行时检查,但这些地方通常是在守门人点,公共方法等。只是FWIW。private


答案 2

正式地说,你的同事是对的:

  • 如果或相应的跟踪不够热,则解释字节码,并创建额外的堆栈帧someMethod()

  • 如果从热点在第 9 个深度级别上调用,则由于 MaxInlineLevel JVM 选项,不应内联调用someMethod()requireNonNull()

  • 如果由于上述任何原因该方法未内联,则T.J. Crowder的论点将发挥作用,如果您使用串联来生成错误消息

  • 即使是内联的,JVM 也会浪费时间和空间来执行此操作。requireNonNull()

另一方面,有JVM选项,它禁止内联太大(在字节码中)的方法。该方法的字节码由其自身计数,没有在此方法中调用的方法的记帐大小。因此,将代码片段提取到独立方法中有时可能很有用,在此示例中,已经为您进行了此提取。FreqInlineSizerequireNonNull()


推荐