java 方法调用有多昂贵

2022-08-31 11:38:14

我是一个初学者,我总是读到重复代码是不好的。但是,似乎为了不这样做,您通常必须进行额外的方法调用。假设我有以下课程

public class BinarySearchTree<E extends Comparable<E>>{
    private BinaryTree<E> root;
    private final BinaryTree<E> EMPTY = new BinaryTree<E>();
    private int count;
    private Comparator<E> ordering;

    public BinarySearchTree(Comparator<E> order){
        ordering = order;
        clear();
    }

    public void clear(){
        root = EMPTY;
        count = 0;
    }
}

对我来说,将 clear() 方法中的两行复制并粘贴到构造函数中,而不是调用实际方法会更优化吗?如果是这样,它会产生多大的差异?如果我的构造函数进行了 10 次方法调用,每个调用都只是将实例变量设置为一个值,该怎么办?什么是最佳编程实践?


答案 1

对我来说,将 clear() 方法中的两行复制并粘贴到构造函数中,而不是调用实际方法会更优化吗?

编译器可以执行该优化。JVM也是如此。编译器编写者和 JVM 作者使用的术语是“内联扩展”。

如果是这样,它会产生多大的差异?

测量它。通常,您会发现它没有区别。如果您认为这是一个性能热点,那么您找错了地方;这就是为什么你需要测量它。

如果我的构造函数进行了 10 次方法调用,每个调用都只是将实例变量设置为一个值,该怎么办?

同样,这取决于生成的字节码和 Java 虚拟机执行的任何运行时优化。如果编译器/JVM 可以内联方法调用,它将执行优化以避免在运行时创建新堆栈帧的开销。

什么是最佳编程实践?

避免过早优化。最佳做法是编写可读且设计良好的代码,然后针对应用程序中的性能热点进行优化。


答案 2

其他人对优化的看法是绝对正确的。

从性能的角度来看,没有理由内联该方法。如果这是性能问题,JVM 中的 JIT 将内联它。在java中,方法调用非常接近免费,不值得考虑它。

话虽如此,这里有一个不同的问题。也就是说,从构造函数调用可重写的方法(即,不是 、 或 ) 的方法)糟糕的编程实践。(《有效的Java》,第2版,第89页,标题为“继承的设计和文档,否则禁止继承”)finalstaticprivate

如果有人添加了一个名为 的子类,该子类用如下代码覆盖所有公共方法,会发生什么情况:BinarySearchTreeLoggingBinarySearchTree

public void clear(){
  this.callLog.addCall("clear");
  super.clear();
}

那么将永远无法建造!问题是,当构造函数正在运行时,但是被调用的是被覆盖的构造函数,您将获得一个 .LoggingBinarySearchTreethis.callLognullBinarySearchTreeclearNullPointerException

请注意,Java 和 C++在这里有所不同:在C++中,调用方法的超类构造函数最终调用超类中定义的构造函数,而不是被覆盖的方法。在两种语言之间切换的人有时会忘记这一点。virtual

鉴于此,我认为在你的情况下,从构造函数调用时内联方法可能更干净,但一般来说,在Java中,你应该继续进行所需的所有方法调用。clear


推荐