Java静态调用比非静态调用贵还是便宜?

Java静态调用比非静态调用贵还是便宜?,java,performance,premature-optimization,Java,Performance,Premature Optimization,是否存在这样或那样的性能优势?它是特定于编译器/虚拟机的吗?我正在使用Hotspot。静态调用和非静态调用的性能差异不太可能对应用程序产生影响。记住“过早优化是万恶之源”。理论上,成本更低 即使创建了对象的实例,静态初始化也将完成,而静态方法不会执行通常在构造函数中完成的任何初始化 然而,我还没有测试过这个 首先:你不应该根据性能来选择静态还是非静态 第二:在实践中,这不会有任何区别。Hotspot可能会选择优化的方式,使一种方法的静态调用更快,而另一种方法的非静态调用更快 第三:围绕静态与非静

是否存在这样或那样的性能优势?它是特定于编译器/虚拟机的吗?我正在使用Hotspot。

静态调用和非静态调用的性能差异不太可能对应用程序产生影响。记住“过早优化是万恶之源”。

理论上,成本更低

即使创建了对象的实例,静态初始化也将完成,而静态方法不会执行通常在构造函数中完成的任何初始化


然而,我还没有测试过这个

首先:你不应该根据性能来选择静态还是非静态

第二:在实践中,这不会有任何区别。Hotspot可能会选择优化的方式,使一种方法的静态调用更快,而另一种方法的非静态调用更快

第三:围绕静态与非静态的许多神话都基于非常老的JVM(它没有在热点附近做任何优化),或者是一些关于C++的记忆琐事(动态调用比静态调用使用更多的内存访问)。(内联的候选者也是如此),并且不需要任何空值检查。HotSpot对实例方法进行了一系列非常酷的优化,这些优化可能会抵消这些优势,但它们可能是静态调用速度更快的原因


然而,这不应该影响你的设计——以最易读、最自然的方式编写代码——只要你有正当理由(你几乎永远不会这样做),就只需要担心这种微观优化。

正如之前的海报所说:这似乎是一种过早的优化

但是,有一个区别(非静态调用需要将被调用对象额外推送到操作数堆栈上这一事实的一部分):

由于无法覆盖静态方法,在运行时将不会对静态方法调用进行任何虚拟查找。在某些情况下,这可能会导致明显的差异


字节码级别上的区别在于,非静态方法调用是通过执行的,或者静态方法调用是通过执行的。

可能会有区别,对于任何特定的代码段,可能会出现任何一种情况,甚至可能会随着JVM的小版本而改变


正如Jon所指出的,静态方法是不能被重写的,因此在一个足够幼稚的Java运行时,简单地调用静态方法可能比调用实例方法更快


但是,即使假设您正处于这样一个时刻,您关心的是如何搞乱您的设计以节省几纳秒,这也会带来另一个问题:您是否需要方法重写您自己?如果您将代码更改为使实例方法变为静态方法以节省几纳秒,然后在此处和那里转换并实现除此之外,您自己的调度程序的效率几乎肯定会低于Java运行时中内置的调度程序。

它是特定于编译器/虚拟机的

  • 理论上,可以进行静态调用 效率稍微高一点,因为 不需要执行虚拟函数 查找,它还可以避免 隐藏的“这个”的头顶 参数
  • 在实践中,许多编译器将 无论如何都要优化这个
因此,除非您已经确定这是应用程序中真正关键的性能问题,否则可能不值得担心

但是,我发现,在以下情况下,此优化可以显著提高性能:

  • 方法在没有内存访问的情况下执行非常简单的数学计算
  • 方法在紧密的内部循环中每秒被调用数百万次
  • CPU限制的应用程序,其中每一点性能都很重要
如果上述内容适用于您,则可能值得测试


使用静态方法还有一个很好的理由(可能更重要!)如果该方法实际上具有静态语义(即逻辑上没有连接到类的给定实例)然后,将其设置为静态以反映这一事实是有意义的。有经验的Java程序员会注意到静态修饰符,并立即认为“啊哈!这个方法是静态的,因此它不需要实例,并且可能不会操纵实例特定的状态”。因此,您将有效地传达方法的静态性质。…

对于一个方法是否应该是静态的决策,性能方面应该是无关的。如果您有性能问题,将许多方法设置为静态不会节省时间。也就是说,静态方法几乎肯定不会比任何实例慢方法,在大多数情况下稍微快一点:

1.)静态方法不是多态性的,因此JVM在查找要执行的实际代码时要做的决定较少。这在Hotspot时代是一个没有实际意义的点,因为Hotspot将优化只有一个实现站点的实例方法调用,因此它们将执行相同的操作

2.)另一个细微的区别是静态方法显然没有“this”引用。这导致堆栈帧比具有相同签名和主体的实例方法的堆栈帧小一个插槽(“这”放在字节码级别的局部变量的插槽0中,而对于静态方法,插槽0用于方法的第一个参数)。

四年后……

好的,为了一劳永逸地解决这个问题,我编写了一个基准测试,它显示了不同类型的调用(虚拟、非虚拟、静态)之间的比较

我运行了它,这就是我得到的:

(迭代次数越多越好。)

正如所料,vir
    Success time: 3.12 memory: 320576 signal:0
  Name          |  Iterations
    VirtualTest |  128009996
 NonVirtualTest |  301765679
     StaticTest |  352298601
Done.
import java.io.*;

class StaticVsInstanceBenchmark
{
    public static void main( String[] args ) throws Exception
    {
        StaticVsInstanceBenchmark program = new StaticVsInstanceBenchmark();
        program.run();
    }

    static final int DURATION = 1000;

    public void run() throws Exception
    {
        doBenchmark( new VirtualTest( new ClassWithVirtualMethod() ), 
                     new NonVirtualTest( new ClassWithNonVirtualMethod() ), 
                     new StaticTest() );
    }

    void doBenchmark( Test... tests ) throws Exception
    {
        System.out.println( "  Name          |  Iterations" );
        doBenchmark2( devNull, 1, tests ); //warmup
        doBenchmark2( System.out, DURATION, tests );
        System.out.println( "Done." );
    }

    void doBenchmark2( PrintStream printStream, int duration, Test[] tests ) throws Exception
    {
        for( Test test : tests )
        {
            long iterations = runTest( duration, test );
            printStream.printf( "%15s | %10d\n", test.getClass().getSimpleName(), iterations );
        }
    }

    long runTest( int duration, Test test ) throws Exception
    {
        test.terminate = false;
        test.count = 0;
        Thread thread = new Thread( test );
        thread.start();
        Thread.sleep( duration );
        test.terminate = true;
        thread.join();
        return test.count;
    }

    static abstract class Test implements Runnable
    {
        boolean terminate = false;
        long count = 0;
    }

    static class ClassWithStaticStuff
    {
        static int staticDummy;
        static void staticMethod() { staticDummy++; }
    }

    static class StaticTest extends Test
    {
        @Override
        public void run()
        {
            for( count = 0;  !terminate;  count++ )
            {
                ClassWithStaticStuff.staticMethod();
            }
        }
    }

    static class ClassWithVirtualMethod implements Runnable
    {
        int instanceDummy;
        @Override public void run() { instanceDummy++; }
    }

    static class VirtualTest extends Test
    {
        final Runnable runnable;

        VirtualTest( Runnable runnable )
        {
            this.runnable = runnable;
        }

        @Override
        public void run()
        {
            for( count = 0;  !terminate;  count++ )
            {
                runnable.run();
            }
        }
    }

    static class ClassWithNonVirtualMethod
    {
        int instanceDummy;
        final void nonVirtualMethod() { instanceDummy++; }
    }

    static class NonVirtualTest extends Test
    {
        final ClassWithNonVirtualMethod objectWithNonVirtualMethod;

        NonVirtualTest( ClassWithNonVirtualMethod objectWithNonVirtualMethod )
        {
            this.objectWithNonVirtualMethod = objectWithNonVirtualMethod;
        }

        @Override
        public void run()
        {
            for( count = 0;  !terminate;  count++ )
            {
                objectWithNonVirtualMethod.nonVirtualMethod();
            }
        }
    }

    static final PrintStream devNull = new PrintStream( new OutputStream() 
    {
        public void write(int b) {}
    } );
}
Public class MyDao {

   private String sql = "select * from MY_ITEM";

   public List<MyItem> getAllItems() {
       springJdbcTemplate.query(sql, new MyRowMapper());
   };
};
Public class MyDao {

   private static RowMapper myRowMapper = new MyRowMapper();
   private String sql = "select * from MY_ITEM";

   public List<MyItem> getAllItems() {
       springJdbcTemplate.query(sql, myRowMapper);
   };
};
java -jar target/benchmark.jar

# -- snip --

Benchmark                        Mode  Cnt          Score         Error  Units
MyBenchmark.testInstanceMethod  thrpt  200  414036562.933 ± 2198178.163  ops/s
MyBenchmark.testStaticMethod    thrpt  200  417194553.496 ± 1055872.594  ops/s
package ca.junctionbox.svsi;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;

class InstanceSum {
    public int sum(final int a, final int b) {
        return a + b;
    }
}

class StaticSum {
    public static int sum(final int a, final int b) {
        return a + b;
    }
}

public class MyBenchmark {
    private static final InstanceSum impl = new InstanceSum();

    @State(Scope.Thread)
    public static class Input {
        public int a = 1;
        public int b = 2;
    }

    @Benchmark
    public void testStaticMethod(Input i, Blackhole blackhole) {
        int sum = StaticSum.sum(i.a, i.b);
        blackhole.consume(sum);
    }

    @Benchmark
    public void testInstanceMethod(Input i, Blackhole blackhole) {
        int sum = impl.sum(i.a, i.b);
        blackhole.consume(sum);
    }
}