Java 静态调用比非静态调用成本更高还是更低?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3805155/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 05:15:41  来源:igfitidea点击:

Are Java static calls more or less expensive than non-static calls?

javaperformancepremature-optimization

提问by Andy Faibishenko

Is there any performance benefit one way or another? Is it compiler/VM specific? I am using Hotspot.

是否有任何性能优势?它是编译器/虚拟机特定的吗?我正在使用热点。

回答by DJClayworth

It is unbelievably unlikely that any difference in the performance of static versus non-static calls is making a difference in your application. Remember that "premature optimization is the root of all evil".

令人难以置信的是,静态调用与非静态调用在性能上的任何差异都不太可能对您的应用程序产生影响。请记住“过早的优化是万恶之源”。

回答by Powerlord

In theory, less expensive.

理论上,更便宜。

Static initialization is going to be done even if you create an instance of the object, whereas static methods will not do any initialization normally done in a constructor.

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

However, I haven't tested this.

不过,我还没有测试过这个。

回答by Anon

First: you shouldn't be making the choice of static vs non-static on the basis of performance.

第一:您不应该根据性能来选择静态还是非静态。

Second: in practice, it won't make any difference. Hotspot may choose to optimize in ways that make static calls faster for one method, non-static calls faster for another.

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

Third: much of the mythos surrounding static versus non-static are based either on very old JVMs (which did not do anywhere near the optimization that Hotspot does), or some remembered trivia about C++ (in which a dynamic call uses onemore memory access than a static call).

第三:多周边静态与非静态的神话的或者基于非常古老的JVM(其中没有任何地方做热点做了优化附近),或关于C的一些记忆琐事++(其中一个动态调用使用一个更大的内存访问而不是静态调用)。

回答by Jon Skeet

Well, static calls can'tbe overridden (so are always candidates for inlining), and don't require any nullity checks. HotSpot does a bunch of cool optimizations for instance methods which may well negate these advantages, but they're possiblereasons why a static call may be faster.

好吧,静态调用不能被覆盖(所以总是内联的候选者),并且不需要任何空值检查。HotSpot 对实例方法进行了一系列很酷的优化,这些优化可能会抵消这些优势,但它们可能是静态调用可能更快的原因。

However, that shouldn't affect your design - code in the most readable, natural way - and only worry about this sort of micro-optimization if you have just cause (which you almost neverwill).

但是,这不应该影响您的设计 - 以最易读、最自然的方式编写代码 - 如果您有正当理由(您几乎永远不会),只需要担心这种微优化。

回答by aioobe

As previous posters have said: This seems like a premature optimization.

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

However, there is one difference (a part from the fact that non-static invokations require an additional push of a callee-object onto the operand stack):

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

Since static methods can't be overridden, there will not be any virtual lookupsin runtime for a static method call. This may result in an observable difference under some circumstances.

由于无法覆盖静态方法,因此在运行时不会为静态方法调用进行任何虚拟查找。在某些情况下,这可能会导致可观察到的差异。

The difference on a byte-code level is that a non-static method call is done through INVOKEVIRTUAL, INVOKEINTERFACEor INVOKESPECIALwhile a static method call is done through INVOKESTATIC.

字节码级别的区别在于,非静态方法调用是通过 完成的INVOKEVIRTUALINVOKEINTERFACE或者INVOKESPECIAL静态方法调用是通过 完成的INVOKESTATIC

回答by Michael Borgwardt

There might be a difference, and it might go either way for any particular piece of code, and it might change with even a minor release of the JVM.

可能存在差异,对于任何特定的代码段,它可能会采用任何一种方式,甚至可能会随着 JVM 的一个次要版本而改变。

This is most definitely part of the 97% of small efficiencies that you should forget about.

这绝对是您应该忘记的 97% 小效率的一部分。

回答by Ken

As Jon notes, static methods can't be overridden, so simply invokinga static method may be -- on a sufficiently naive Java runtime -- faster than invokingan instance method.

正如 Jon 所指出的,静态方法不能被覆盖,所以简单地调用一个静态方法可能比调用一个实例方法更快——在一个足够简单的 Java 运行时。

But then, even assuming you're at the point where you care about messing up your design to save a few nanoseconds, that just brings up another question: will you need method overriding yourself? If you change your code around to make an instance method into a static method to save a nanosecond here and there, and then turn around and implement your own dispatcher on top of that, yours is almost certainly going to be less efficient than the one built into your Java runtime already.

但是,即使假设您正处于关心将设计搞砸以节省几纳秒的地步,那只会带来另一个问题:您是否需要覆盖自己的方法?如果您更改代码以将实例方法转换为静态方法以在这里和那里节省一纳秒,然后转身并在此基础上实现您自己的调度程序,那么您的调度程序几乎肯定会比构建的效率低已经进入您的 Java 运行时。

回答by mikera

It is compiler/VM specific.

它是特定于编译器/VM 的。

  • In theory, a static call can be made slightly more efficient because it doesn't need to do a virtual function lookup, and it can also avoid the overhead of the hidden "this" parameter.
  • In practice, many compilers will optimize this out anyway.
  • 理论上,静态调用可以稍微高效一点,因为它不需要做虚函数查找,而且还可以避免隐藏的“this”参数的开销。
  • 实际上,许多编译器无论如何都会优化它。

Hence it's probably not worth bothering about unless you have identified this as a truly critical performance issue in your application. Premature optimization is the root of all evil etc...

因此,除非您已将其确定为应用程序中真正关键的性能问题,否则它可能不值得操心。过早的优化是万恶之源等等...

However I haveseen this optimization give a substantial performance increase in the following situation:

但是,我已经看到这种优化在以下情况下可以显着提高性能:

  • Method performing a very simple mathematical calculation with no memory accesses
  • Method being invoked millionsof times per second in a tight inner loop
  • CPU bound application where every bit of performance mattered
  • 执行非常简单的数学计算的方法,无需访问内存
  • 方法在紧密的内部循环中每秒被调用数百万
  • CPU 密集型应用程序,其中每一点性能都很重要

If the above applies to you, it may be worth testing.

如果上述情况适用于您,则可能值得测试。

There is also one other good (and potentially even more important!) reason to use a static method - if the method actually has static semantics (i.e. logically is not connected to a given instance of the class) then it makes sense to make it static to reflect this fact. Experienced Java programmers will then notice the static modifier and immediately think "aha! this method is static so it doesn't need an instance and presumably doesn't manipulate instance specific state". So you will have communicated the static nature of the method effectively....

使用静态方法还有另一个好的(可能甚至更重要!)理由 - 如果该方法实际上具有静态语义(即逻辑上未连接到类的给定实例),那么将其设为静态是有意义的来反映这一事实。有经验的 Java 程序员会注意到 static 修饰符并立即想到“啊哈!这个方法是静态的,所以它不需要实例,并且大概不会操作实例特定的状态”。因此,您将有效地传达了该方法的静态性质......

回答by Durandal

For the decision if a method should be static, the performance aspect should be irrelevant. If you have a performance problem, making lots of methods static isn't going to save the day. That said, static methods are almost certainly not slowerthan any instance method, in most cases marginally faster:

对于一个方法是否应该是静态的决定,性能方面应该是无关紧要的。如果您遇到性能问题,将大量方法设为静态并不能挽救局面。也就是说,静态方法几乎肯定不会比任何实例方法,在大多数情况下都稍微快一点

1.) Static methods are not polymorphic, so the JVM has less decisions to make to find the actual code to execute. This is a moot point in the Age of Hotspot, since Hotspot will optimize instance method calls that have only one implementation site, so they will perform the same.

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

2.) Another subtle difference is that static methods obviously have no "this" reference. This results in a stack frame one slot smaller than that of an instance method with the same signature and body ("this" is put in slot 0 of the local variables on the bytecode level, whereas for static methods slot 0 is used for the first parameter of the method).

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

回答by Mike Nakis

Four years later...

四年后...

Okay, in the hope of settling this question once and forever, I have written a benchmark which shows how the different kinds of calls (virtual, non-virtual, static) compare to each other.

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

I ran it on ideone, and this is what I got:

在 ideone 上运行它,这就是我得到的:

(Larger number of iterations is better.)

(迭代次数越多越好。)

    Success time: 3.12 memory: 320576 signal:0
  Name          |  Iterations
    VirtualTest |  128009996
 NonVirtualTest |  301765679
     StaticTest |  352298601
Done.

As expected, virtual method calls are the slowest, non-virtual method calls are faster, and static method calls are even faster.

正如预期的那样,虚方法调用最慢,非虚方法调用更快,静态方法调用甚至更快。

What I did not expect was the differences to be so pronounced: Virtual method calls were measured to run at less than halfthe speed of non-virtual method calls, which in turn were measured to run a whole 15% slowerthan static calls. That's what these measurements show; the actual differences must in fact be slightly more pronounced, since for each virtual, nonvirtual, and static method call, my benchmarking code has an additional constant overhead of incrementing one integer variable, checking a boolean variable, and looping if not true.

我没想到差异如此明显:经测量,虚拟方法调用的运行速度不到非虚拟方法调用的一半,而后者的运行速度比静态调用慢 15%。这就是这些测量显示的内容;实际上,实际差异必须稍微明显一些,因为对于每个虚拟、非虚拟和静态方法调用,我的基准测试代码都有一个额外的常量开销,即增加一个整数变量、检查一个布尔变量,如果不为真则循环。

I suppose the results will vary from CPU to CPU, and from JVM to JVM, so give it a try and see what you get:

我想结果会因 CPU 和 CPU 以及 JVM 到 JVM 的不同而不同,所以试一试,看看你会得到什么:

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) {}
    } );
}

It is worth noting that this performance difference is only applicable to code which does nothing other than invoking parameterless methods. Whatever other code you have between the invocations will dilute the differences, and this includes parameter passing. Actually, the 15% difference between static and nonvirtual calls is probably explained in fullby the fact that the thispointer does not have to be passed to the static method. So, it would only take a fairly small amount of code doing trivial stuff in between calls for the difference between different kinds of calls to be diluted to the point of having no net impact whatsoever.

值得注意的是,这种性能差异仅适用于除了调用无参数方法之外什么都不做的代码。调用之间的任何其他代码都会淡化差异,这包括参数传递。实际上,静态调用和非虚拟调用之间 15% 的差异可能完全this可以通过指针不必传递给静态方法这一事实来充分解释。因此,在调用之间只需要相当少量的代码做一些琐碎的事情,就可以将不同类型调用之间的差异稀释到没有任何净影响的程度。

Also, virtual method calls exist for a reason; they do have a purpose to serve, and they are implemented using the most efficient means provided by the underlying hardware. (The CPU instruction set.) If, in your desire to eliminate them by replacing them with nonvirtual or static calls, you end up having to add as much as an iota of extra code to emulate their functionality, then your resulting net overhead is bound to be not less, but more. Quite possibly, much, much, unfathomably much, more.

此外,存在虚方法调用是有原因的;它们确实有服务的目的,并且它们是使用底层硬件提供的最有效的方式实现的。(CPU 指令集。)如果您希望通过用非虚拟或静态调用替换它们来消除它们,最终不得不添加尽可能多的额外代码来模拟它们的功能,那么您产生的净开销是有限的不是更少,而是更多。很可能,很多,很多,深不可测的很多,更多。