在Java中使用final关键字可以提高性能吗?

时间:2022-08-20 21:03:43

In Java we see lots of places where the final keyword can be used but its use is uncommon.

在Java中,我们看到很多地方可以使用final关键字,但它的使用并不常见。

For example:

例如:

String str = "abc";
System.out.println(str);

In the above case, str can be final but this is commonly left off.

在上面的例子中,str可以是final,但是这通常被省略。

When a method is never going to be overridden we can use final keyword. Similarly in case of a class which is not going to be inherited.

当一个方法永远不会被重写时,我们可以使用final关键字。类似地,对于一个不被继承的类。

Does the use of final keyword in any or all of these cases really improve performance? If so, then how? Please explain. If the proper use of final really matters for performance, what habits should a Java programmer develop to make best use of the keyword?

在这些情况中使用final关键字真的能提高性能吗?如果是这样,那么如何?请解释一下。如果正确地使用final真的对性能很重要,那么Java程序员应该开发哪些习惯来充分利用关键字呢?

11 个解决方案

#1


229  

Usually not. For virtual methods, HotSpot keeps track of whether the method has actually been overridden, and is able to perform optimizations such as inlining on the assumption that a method hasn't been overridden - until it loads a class which overrides the method, at which point it can undo (or partially undo) those optimizations.

通常不会。虚拟方法,热点跟踪是否已经覆盖的方法,并能够执行优化如内联方法假设没有覆盖,直到它加载一个类覆盖方法,这时它可以撤销(或部分撤销)这些优化。

(Of course, this is assuming you're using HotSpot - but it's by far the most common JVM, so...)

(当然,这是假设您正在使用HotSpot——但它是目前为止最常见的JVM,所以…)

To my mind you should use final based on clear design and readability rather than for performance reasons. If you want to change anything for performance reasons, you should perform appropriate measurements before bending the clearest code out of shape - that way you can decide whether any extra performance achieved is worth the poorer readability/design. (In my experience it's almost never worth it; YMMV.)

在我看来,你应该基于清晰的设计和可读性来使用final,而不是出于性能原因。如果您想为了性能的原因改变任何东西,您应该在弯曲最清晰的代码之前执行适当的度量——这样您就可以决定是否获得了额外的性能,这是否值得更差的可读性/设计。(以我的经验来看,这几乎是不值得的;YMMV)。

EDIT: As final fields have been mentioned, it's worth bringing up that they are often a good idea anyway, in terms of clear design. They also change the guaranteed behaviour in terms of cross-thread visibility: after a constructor has completed, any final fields are guaranteed to be visible in other threads immediately. This is probably the most common use of final in my experience, although as a supporter of Josh Bloch's "design for inheritance or prohibit it" rule of thumb, I should probably use final more often for classes...

编辑:正如最后提到的,在清晰的设计方面,它们通常是一个好主意,这是值得提出的。它们还会根据交叉线程的可见性改变有保证的行为:在构造函数完成之后,任何最终字段都保证在其他线程中立即可见。在我的经验中,这可能是final最常用的用法,尽管作为Josh Bloch的“继承设计或禁止它”经验法则的支持者,我应该更经常地在类中使用final……

#2


60  

Short answer: don't worry about it!

简短的回答:别担心!

Long answer:

长一点的回答:

When talking about final local variables keep in mind that using the keyword final will help the compiler optimize the code statically, which may in the end result in faster code. For example, the final Strings a + b in the example below are concatenated statically (at compile time).

在讨论最终局部变量时,请记住使用关键字final将有助于编译器静态地优化代码,这最终可能导致代码更快。例如,下面示例中的最后一个字符串a + b被静态连接(在编译时)。

public class FinalTest {

    public static final int N_ITERATIONS = 1000000;

    public static String testFinal() {
        final String a = "a";
        final String b = "b";
        return a + b;
    }

    public static String testNonFinal() {
        String a = "a";
        String b = "b";
        return a + b;
    }

    public static void main(String[] args) {
        long tStart, tElapsed;

        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method with finals took " + tElapsed + " ms");

        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testNonFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method without finals took " + tElapsed + " ms");

    }

}

The result?

结果呢?

Method with finals took 5 ms
Method without finals took 273 ms

Tested on Java Hotspot VM 1.7.0_45-b18.

在Java Hotspot VM 1.7.0_45-b18上测试。

So how much is the actual performance improvement? I don't dare say. In most cases probably marginal (~270 nanoseconds in this synthetic test because the string concatenation is avoided altogether - a rare case), but in highly optimized utility code it might be a factor. In any case the answer to the original question is yes, it might improve performance, but marginally at best.

那么实际性能提高了多少?我不敢说。在大多数情况下可能是边际的(在这个合成测试中大约270纳秒,因为字符串连接是完全避免的——这种情况很少见),但是在高度优化的实用程序代码中,它可能是一个因素。在任何情况下,最初的问题的答案是肯定的,它可能会提高性能,但充其量只是勉强。

Compile-time benefits aside, I could not find any evidence that the use of the keyword final has any measurable effect on performance.

除了编译时的好处之外,我找不到任何证据表明使用关键字final会对性能产生任何可测量的影响。

Edit: When decompiling the code using javap -c FinalTest.class the following instructions are obtained:

编辑:使用javap -c FinalTest对代码进行反编译时。课程如下:

 public static java.lang.String testFinal();
    Code:
       0: ldc           #16                 // String a
       2: astore_0
       3: ldc           #18                 // String b
       5: astore_1
       6: ldc           #20                 // String ab
       8: areturn

  public static java.lang.String testNonFinal();
    Code:
       0: ldc           #16                 // String a
       2: astore_0
       3: ldc           #18                 // String b
       5: astore_1
       6: new           #24                 // class java/lang/StringBuilder
       9: dup
      10: aload_0
      11: invokestatic  #26                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
      14: invokespecial #32                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      17: aload_1
      18: invokevirtual #35                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: invokevirtual #39                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      24: areturn

As the testFinal() method has less instructions than testNonFinal(), it will be faster.

由于testFinal()方法的指令比testNonFinal()少,所以它会更快。

#3


44  

YES it can. Here is an instance where final can boost performance:

是的,它可以。这里有一个例子,最终可以提高性能:

Conditional compilation is a technique in which lines of code are not compiled into the class file based on a particular condition. This can be used to remove tons of debugging code in a production build.

条件编译是一种技术,在这种技术中,代码行不会基于特定的条件编译到类文件中。这可以用于删除生产构建中的大量调试代码。

consider the following:

考虑以下:

public class ConditionalCompile {

  private final static boolean doSomething= false;

    if (doSomething) {
       // do first part. 
    }

    if (doSomething) {
     // do second part. 
    }

    if (doSomething) {     
      // do third part. 
    }

    if (doSomething) {
    // do finalization part. 
    }
}

By converting the doSomething attribute into a final attribute, you have told the compiler that whenever it sees doSomething, it should replace it with false as per the compile-time substitution rules. The first pass of the compiler changes the code to something like this:

通过将doSomething属性转换为最终属性,您已经告诉编译器,每当它看到doSomething时,它应该根据编译时替换规则将其替换为false。编译器的第一次传递将代码更改为如下内容:

public class ConditionalCompile {

  private final static boolean doSomething= false;

    if (false){
       // do first part. 
    }

    if (false){
     // do second part. 
    }

    if (false){
      // do third part. 
    }

    if (false){
    // do finalization part. 

    }
}

Once this is done, the compiler takes another look at it and sees that there are unreachable statements in the code. Since you are working with a top-quality compiler, it doesn't like all those unreachable byte codes. So it removes them, and you end up with this:

完成之后,编译器再看一遍,发现代码中有不可访问的语句。因为您正在使用高质量的编译器,所以它不喜欢所有那些不可访问的字节代码。所以它去掉了它们,你就得到了这个:

public class ConditionalCompile {


  private final static boolean doSomething= false;

  public static void someMethodBetter( ) {

    // do first part. 

    // do second part. 

    // do third part. 

    // do finalization part. 

  }
}

thus reducing any excessive codes, or any unnecessary conditional checking.

从而减少任何多余的代码,或任何不必要的条件检查。

Edit: As an example, let's take the following code:

编辑:以以下代码为例:

public class Test {
    public static final void main(String[] args) {
        boolean x = false;
        if (x) {
            System.out.println("x");
        }
        final boolean y = false;
        if (y) {
            System.out.println("y");
        }
        if (false) {
            System.out.println("z");
        }
    }
}

When compiling this code with Java 8 and decompiling with javap -c Test.class we get:

当使用Java 8编译此代码并使用Java -c测试进行反编译时。类我们得到:

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static final void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: ifeq          14
       6: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       9: ldc           #22                 // String x
      11: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      14: iconst_0
      15: istore_2
      16: return
}

We can note that compiled code includes only the non-final variable x. This prooves that final variables have impact on performances, at least for this simple case.

我们可以注意到,编译后的代码只包含非最终变量x,这证明最终变量对性能有影响,至少对于这个简单的例子是这样的。

#4


36  

According to IBM - it doesnt for classes or methods.

根据IBM的说法,它不支持类或方法。

http://www.ibm.com/developerworks/java/library/j-jtp04223.html

http://www.ibm.com/developerworks/java/library/j-jtp04223.html

#5


12  

You are really asking about two (at least) different cases:

你问的是两个(至少)不同的情况:

  1. final for local variables
  2. 最后对局部变量
  3. final for methods/classes
  4. 最后的方法/类

Jon Skeet has already answered 2). About 1):

Jon Skeet已经回答了2)。

I don't think it makes a difference; for local variables, the compiler can deduce whether the variable is final or not (simply by checking whether it is assigned more than once). So if the compiler wanted to optimize variables that are only assigned once, it can do so no matter whether the variable is actually declared final or not.

我不认为这有什么区别;对于局部变量,编译器可以推断变量是否为最终变量(只需检查它是否被分配了多次)。因此,如果编译器想要优化只分配一次的变量,那么不管变量是否被声明为final,它都可以这样做。

final might make a difference for protected/public class fields; there it's very difficult for the compiler to find out if the field is being set more than once, as it could happen from a different class (which may not even have been loaded). But even then the JVM could use the technique Jon describes (optimize optimistically, revert if a class is loaded which does change the field).

final可能会对受保护/公共类字段产生影响;在那里,编译器很难发现字段是否被设置不止一次,因为它可能来自不同的类(甚至可能没有加载)。但是,即使这样,JVM也可以使用Jon描述的技术(乐观地优化,如果装载了更改字段的类,则恢复)。

In summary, I don't see any reason why it should help performance. So this kind of micro-optimization is unlikely to help. You could try benchmarking it to make sure, but I doubt it will make a difference.

总而言之,我看不出它有什么理由有助于性能。所以这种微优化是不可能有帮助的。你可以试着对它进行基准测试以确保它的正确性,但我怀疑它是否会产生影响。

Edit:

编辑:

Actually, according to Timo Westkämper's answer, final can improve performance for class fields in some cases. I stand corrected.

实际上,根据Timo Westkamper的回答,final在某些情况下可以提高类字段的性能。我认错。

#6


5  

Note: Not a java expert

注意:不是java专家。

If I remember my java correctly, there would be very little way to improve performance using the final keyword. I've always known it to exist for "good code" - design and readability.

如果我没记错我的java,那么使用final关键字来提高性能的方法就很少了。我一直都知道它的存在是因为“良好的代码”——设计和可读性。

#7


4  

I am amazed that no one has actually posted some real code that is de-compiled to prove that there is at least some minor difference.

我感到惊讶的是,实际上还没有人发布过一些经过反编译的真实代码,以证明至少存在一些细微的差异。

For the reference this has been tested against javac version 8, 9 and 10.

对于引用,已经对javac版本8、9和10进行了测试。

Suppose this method:

假设这个方法:

public static int test() {
    /* final */ Object left = new Object();
    Object right = new Object();

    return left.hashCode() + right.hashCode();
}

Compiling this code as it is, produces the exact same byte code as when final would have been present (final Object left = new Object();).

编译此代码时,生成的字节码与出现final时的字节码完全相同(final对象左侧= new Object();)。

But this one:

但是这一次:

public static int test() {
    /* final */ int left = 11;
    int right = 12;
    return left + right;
}

Produces:

生产:

   0: bipush        11
   2: istore_0
   3: bipush        12
   5: istore_1
   6: iload_0
   7: iload_1
   8: iadd
   9: ireturn

Leaving final to be present produces:

离开最终到现在产生:

   0: bipush        12
   2: istore_1
   3: bipush        11
   5: iload_1
   6: iadd
   7: ireturn

The code is pretty much self-explanatory, in case there is a compile time constant, it will be loaded directly onto the operand stack (it will not be stored into local variables array like the previous example does via bipush 12; istore_0; iload_0) - which sort of makes sense since no one can change it.

代码非常简单,如果有一个编译时常量,它将直接加载到操作数堆栈中(它不会像前面的示例通过bipush 12那样存储到本地变量数组中);istore_0;这是有意义的,因为没有人可以改变它。

On the other hand why in the second case the compiler does not produce istore_0 ... iload_0 is beyond me, it's not like that slot 0 is used in any way (it could shrink the variables array this way, but may be Im missing some internals details, can't tell for sure)

另一方面,为什么在第二种情况下,编译器不产生istore_0…iload_0超出了我的理解范围,它不会以任何方式使用插槽0(它可以以这种方式缩小变量数组,但可能我漏掉了一些内部细节,无法确定)

I was surprised to see such an optimization, considering how little ones javac does. As to should we always use final? I'm not even going to write a JMH test (which I wanted to initially), I am sure that the diff is in the order of ns (if possible to be captured at all). The only place this could be a problem, is when a method could not be inlined because of it's size (and declaring final would shrink that size by a few bytes).

考虑到javac所做的事情是如此之少,看到这样的优化我感到很惊讶。我们应该一直使用final吗?我甚至不打算写一个JMH测试(这是我最初想做的),我确信diff的顺序是ns(如果可能的话)。唯一可能出现问题的地方是,当一个方法不能被内联的时候,因为它的大小(并且声明final会将这个大小缩小几个字节)。

There are two more finals that need to be addressed. First is when a method is final (from a JIT perspective), such a method is monomorphic - and these are the most beloved ones by the JVM.

还有两场决赛需要解决。首先,当一个方法是最终的(从JIT的角度来看)时,这样的方法是单态的——这是JVM最喜欢的方法。

Then there are final instance variables (that must be set in every constructor); these are important as they will guarantee a correctly published reference, as touched a bit here and also specified exactly by the JLS.

然后还有最终的实例变量(必须在每个构造函数中设置);这些都很重要,因为它们将保证正确发布的引用,这里稍微涉及了一点,JLS也明确指定了这些引用。

#8


1  

I'm not an expert but I suppose you should add final keyword to the class or method if it won't be overwritten and leave variables alone. If there will be any way to optimize such things the compiler will do that for you.

我不是专家,但我认为您应该在类或方法中添加final关键字,如果它不被覆盖并且只留下变量。如果有什么方法可以优化这些东西,编译器会为你做这些。

#9


1  

Actually, while testing some OpenGL-related code, I found that using the final modifier on a private field can degrade performance. Here is the start of the class I tested:

实际上,在测试一些与opengl相关的代码时,我发现在私有字段上使用最终修饰符会降低性能。这是我测试的课程的开始:

public class ShaderInput {

    private /* final */ float[] input;
    private /* final */ int[] strides;


    public ShaderInput()
    {
        this.input = new float[10];
        this.strides = new int[] { 0, 4, 8 };
    }


    public ShaderInput x(int stride, float val)
    {
        input[strides[stride] + 0] = val;
        return this;
    }

    // more stuff ...

And this is the method I used to test the performance of various alternatives, amongst which the ShaderInput class:

这是我用来测试各种替代品性能的方法,其中ShaderInput类:

public static void test4()
{
    int arraySize = 10;
    float[] fb = new float[arraySize];
    for (int i = 0; i < arraySize; i++) {
        fb[i] = random.nextFloat();
    }
    int times = 1000000000;
    for (int i = 0; i < 10; ++i) {
        floatVectorTest(times, fb);
        arrayCopyTest(times, fb);
        shaderInputTest(times, fb);
        directFloatArrayTest(times, fb);
        System.out.println();
        System.gc();
    }
}

After the 3rd iteration, with the VM warmed up, I consistently got these figures without the final key word:

在第3次迭代之后,随着VM的预热,我始终得到这些数字,而没有最后一个关键词:

Simple array copy took   : 02.64
System.arrayCopy took    : 03.20
ShaderInput took         : 00.77
Unsafe float array took  : 05.47

With the final keyword:

最后的关键字:

Simple array copy took   : 02.66
System.arrayCopy took    : 03.20
ShaderInput took         : 02.59
Unsafe float array took  : 06.24

Note the figures for the ShaderInput test.

注意ShaderInput测试的图片。

It didn't matter whether I made the fields public or private.

不管我是公开的还是私有的。

Incidentally, there are a few more baffling things. The ShaderInput class outperforms all other variants, even with the final keyword. This is remarkable b/c it basically is a class wrapping a float array, while the other tests directly manipulate the array. Have to figure this one out. May have something to do with ShaderInput's fluent interface.

顺便说一句,还有一些令人困惑的事情。ShaderInput类比所有其他变体都要出色,即使使用final关键字。这是很了不起的b/c,它基本上是一个封装浮动数组的类,而其他测试直接操作数组。必须把这个算出来。可能与ShaderInput流畅的界面有关。

Also System.arrayCopy actually apparently is somewhat slower for small arrays than simply copying elements from one array to the other in a for loop. And using sun.misc.Unsafe (as well as direct java.nio.FloatBuffer, not shown here) performs abysmally.

同时系统。对于小数组来说,arrayCopy实际上比简单地在for循环中将元素从一个数组复制到另一个数组要慢一些。和使用sun.misc。不安全(以及直接java.nio)。FloatBuffer,这里没有显示)执行非常糟糕。

#10


0  

Definately yes, if variable converts as constant,

是的,如果变量转换为常数,

as we know java compiler convert such final variables as constant which possibles

正如我们所知道的,java编译器可以将最终变量转换为常量

as concept of constant java compiler directly replace the value with it's reference at compile time

作为常量java编译器的概念,它在编译时直接将值替换为它的引用

in java variables goes as constant if that is string or primitive type which given without any runtime process

在java中,如果是字符串或基元类型,则变量作为常量,并且没有任何运行时过程

otherwise it just final (non changeble) variable,

否则它就是最终的(不可改变的)变量,

& constatnt use is always faster than reference.

使用时总是比参考要快。

so if possible, use constants in any programming language for better performance

因此,如果可能的话,在任何编程语言中使用常量可以获得更好的性能

#11


-3  

final keyword can be used in five ways in Java.

在Java中,final关键字有五种用法。

  1. A class is final
  2. 一个类是终局的
  3. A reference variable is final
  4. 引用变量是最终的
  5. A local variable is final
  6. 局部变量是最终变量
  7. A method is final
  8. 一个方法是终局的

A class is final: a class is final means we cannot be extended or inheritance means inheritance is not possible.

类是final的:类是final的意思,意味着不能扩展或继承意味着不可能继承。

Similarly - A object is final: some time we does not modified the internal state of object so in such case we can specify the object is final object.object final means not variable also final.

类似地,对象是final:有时我们不修改对象的内部状态,因此在这种情况下,我们可以指定对象是final对象。对象最终意味着不变量也最终。

Once reference variable is made final, it cannot be reassigned to other object. But can change the contents of the object as long as its fields are not final

一旦引用变量成为最终变量,它就不能被重新分配给其他对象。但是只要对象的字段不是最终的,就可以修改对象的内容

#1


229  

Usually not. For virtual methods, HotSpot keeps track of whether the method has actually been overridden, and is able to perform optimizations such as inlining on the assumption that a method hasn't been overridden - until it loads a class which overrides the method, at which point it can undo (or partially undo) those optimizations.

通常不会。虚拟方法,热点跟踪是否已经覆盖的方法,并能够执行优化如内联方法假设没有覆盖,直到它加载一个类覆盖方法,这时它可以撤销(或部分撤销)这些优化。

(Of course, this is assuming you're using HotSpot - but it's by far the most common JVM, so...)

(当然,这是假设您正在使用HotSpot——但它是目前为止最常见的JVM,所以…)

To my mind you should use final based on clear design and readability rather than for performance reasons. If you want to change anything for performance reasons, you should perform appropriate measurements before bending the clearest code out of shape - that way you can decide whether any extra performance achieved is worth the poorer readability/design. (In my experience it's almost never worth it; YMMV.)

在我看来,你应该基于清晰的设计和可读性来使用final,而不是出于性能原因。如果您想为了性能的原因改变任何东西,您应该在弯曲最清晰的代码之前执行适当的度量——这样您就可以决定是否获得了额外的性能,这是否值得更差的可读性/设计。(以我的经验来看,这几乎是不值得的;YMMV)。

EDIT: As final fields have been mentioned, it's worth bringing up that they are often a good idea anyway, in terms of clear design. They also change the guaranteed behaviour in terms of cross-thread visibility: after a constructor has completed, any final fields are guaranteed to be visible in other threads immediately. This is probably the most common use of final in my experience, although as a supporter of Josh Bloch's "design for inheritance or prohibit it" rule of thumb, I should probably use final more often for classes...

编辑:正如最后提到的,在清晰的设计方面,它们通常是一个好主意,这是值得提出的。它们还会根据交叉线程的可见性改变有保证的行为:在构造函数完成之后,任何最终字段都保证在其他线程中立即可见。在我的经验中,这可能是final最常用的用法,尽管作为Josh Bloch的“继承设计或禁止它”经验法则的支持者,我应该更经常地在类中使用final……

#2


60  

Short answer: don't worry about it!

简短的回答:别担心!

Long answer:

长一点的回答:

When talking about final local variables keep in mind that using the keyword final will help the compiler optimize the code statically, which may in the end result in faster code. For example, the final Strings a + b in the example below are concatenated statically (at compile time).

在讨论最终局部变量时,请记住使用关键字final将有助于编译器静态地优化代码,这最终可能导致代码更快。例如,下面示例中的最后一个字符串a + b被静态连接(在编译时)。

public class FinalTest {

    public static final int N_ITERATIONS = 1000000;

    public static String testFinal() {
        final String a = "a";
        final String b = "b";
        return a + b;
    }

    public static String testNonFinal() {
        String a = "a";
        String b = "b";
        return a + b;
    }

    public static void main(String[] args) {
        long tStart, tElapsed;

        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method with finals took " + tElapsed + " ms");

        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testNonFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method without finals took " + tElapsed + " ms");

    }

}

The result?

结果呢?

Method with finals took 5 ms
Method without finals took 273 ms

Tested on Java Hotspot VM 1.7.0_45-b18.

在Java Hotspot VM 1.7.0_45-b18上测试。

So how much is the actual performance improvement? I don't dare say. In most cases probably marginal (~270 nanoseconds in this synthetic test because the string concatenation is avoided altogether - a rare case), but in highly optimized utility code it might be a factor. In any case the answer to the original question is yes, it might improve performance, but marginally at best.

那么实际性能提高了多少?我不敢说。在大多数情况下可能是边际的(在这个合成测试中大约270纳秒,因为字符串连接是完全避免的——这种情况很少见),但是在高度优化的实用程序代码中,它可能是一个因素。在任何情况下,最初的问题的答案是肯定的,它可能会提高性能,但充其量只是勉强。

Compile-time benefits aside, I could not find any evidence that the use of the keyword final has any measurable effect on performance.

除了编译时的好处之外,我找不到任何证据表明使用关键字final会对性能产生任何可测量的影响。

Edit: When decompiling the code using javap -c FinalTest.class the following instructions are obtained:

编辑:使用javap -c FinalTest对代码进行反编译时。课程如下:

 public static java.lang.String testFinal();
    Code:
       0: ldc           #16                 // String a
       2: astore_0
       3: ldc           #18                 // String b
       5: astore_1
       6: ldc           #20                 // String ab
       8: areturn

  public static java.lang.String testNonFinal();
    Code:
       0: ldc           #16                 // String a
       2: astore_0
       3: ldc           #18                 // String b
       5: astore_1
       6: new           #24                 // class java/lang/StringBuilder
       9: dup
      10: aload_0
      11: invokestatic  #26                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
      14: invokespecial #32                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      17: aload_1
      18: invokevirtual #35                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: invokevirtual #39                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      24: areturn

As the testFinal() method has less instructions than testNonFinal(), it will be faster.

由于testFinal()方法的指令比testNonFinal()少,所以它会更快。

#3


44  

YES it can. Here is an instance where final can boost performance:

是的,它可以。这里有一个例子,最终可以提高性能:

Conditional compilation is a technique in which lines of code are not compiled into the class file based on a particular condition. This can be used to remove tons of debugging code in a production build.

条件编译是一种技术,在这种技术中,代码行不会基于特定的条件编译到类文件中。这可以用于删除生产构建中的大量调试代码。

consider the following:

考虑以下:

public class ConditionalCompile {

  private final static boolean doSomething= false;

    if (doSomething) {
       // do first part. 
    }

    if (doSomething) {
     // do second part. 
    }

    if (doSomething) {     
      // do third part. 
    }

    if (doSomething) {
    // do finalization part. 
    }
}

By converting the doSomething attribute into a final attribute, you have told the compiler that whenever it sees doSomething, it should replace it with false as per the compile-time substitution rules. The first pass of the compiler changes the code to something like this:

通过将doSomething属性转换为最终属性,您已经告诉编译器,每当它看到doSomething时,它应该根据编译时替换规则将其替换为false。编译器的第一次传递将代码更改为如下内容:

public class ConditionalCompile {

  private final static boolean doSomething= false;

    if (false){
       // do first part. 
    }

    if (false){
     // do second part. 
    }

    if (false){
      // do third part. 
    }

    if (false){
    // do finalization part. 

    }
}

Once this is done, the compiler takes another look at it and sees that there are unreachable statements in the code. Since you are working with a top-quality compiler, it doesn't like all those unreachable byte codes. So it removes them, and you end up with this:

完成之后,编译器再看一遍,发现代码中有不可访问的语句。因为您正在使用高质量的编译器,所以它不喜欢所有那些不可访问的字节代码。所以它去掉了它们,你就得到了这个:

public class ConditionalCompile {


  private final static boolean doSomething= false;

  public static void someMethodBetter( ) {

    // do first part. 

    // do second part. 

    // do third part. 

    // do finalization part. 

  }
}

thus reducing any excessive codes, or any unnecessary conditional checking.

从而减少任何多余的代码,或任何不必要的条件检查。

Edit: As an example, let's take the following code:

编辑:以以下代码为例:

public class Test {
    public static final void main(String[] args) {
        boolean x = false;
        if (x) {
            System.out.println("x");
        }
        final boolean y = false;
        if (y) {
            System.out.println("y");
        }
        if (false) {
            System.out.println("z");
        }
    }
}

When compiling this code with Java 8 and decompiling with javap -c Test.class we get:

当使用Java 8编译此代码并使用Java -c测试进行反编译时。类我们得到:

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static final void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: ifeq          14
       6: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       9: ldc           #22                 // String x
      11: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      14: iconst_0
      15: istore_2
      16: return
}

We can note that compiled code includes only the non-final variable x. This prooves that final variables have impact on performances, at least for this simple case.

我们可以注意到,编译后的代码只包含非最终变量x,这证明最终变量对性能有影响,至少对于这个简单的例子是这样的。

#4


36  

According to IBM - it doesnt for classes or methods.

根据IBM的说法,它不支持类或方法。

http://www.ibm.com/developerworks/java/library/j-jtp04223.html

http://www.ibm.com/developerworks/java/library/j-jtp04223.html

#5


12  

You are really asking about two (at least) different cases:

你问的是两个(至少)不同的情况:

  1. final for local variables
  2. 最后对局部变量
  3. final for methods/classes
  4. 最后的方法/类

Jon Skeet has already answered 2). About 1):

Jon Skeet已经回答了2)。

I don't think it makes a difference; for local variables, the compiler can deduce whether the variable is final or not (simply by checking whether it is assigned more than once). So if the compiler wanted to optimize variables that are only assigned once, it can do so no matter whether the variable is actually declared final or not.

我不认为这有什么区别;对于局部变量,编译器可以推断变量是否为最终变量(只需检查它是否被分配了多次)。因此,如果编译器想要优化只分配一次的变量,那么不管变量是否被声明为final,它都可以这样做。

final might make a difference for protected/public class fields; there it's very difficult for the compiler to find out if the field is being set more than once, as it could happen from a different class (which may not even have been loaded). But even then the JVM could use the technique Jon describes (optimize optimistically, revert if a class is loaded which does change the field).

final可能会对受保护/公共类字段产生影响;在那里,编译器很难发现字段是否被设置不止一次,因为它可能来自不同的类(甚至可能没有加载)。但是,即使这样,JVM也可以使用Jon描述的技术(乐观地优化,如果装载了更改字段的类,则恢复)。

In summary, I don't see any reason why it should help performance. So this kind of micro-optimization is unlikely to help. You could try benchmarking it to make sure, but I doubt it will make a difference.

总而言之,我看不出它有什么理由有助于性能。所以这种微优化是不可能有帮助的。你可以试着对它进行基准测试以确保它的正确性,但我怀疑它是否会产生影响。

Edit:

编辑:

Actually, according to Timo Westkämper's answer, final can improve performance for class fields in some cases. I stand corrected.

实际上,根据Timo Westkamper的回答,final在某些情况下可以提高类字段的性能。我认错。

#6


5  

Note: Not a java expert

注意:不是java专家。

If I remember my java correctly, there would be very little way to improve performance using the final keyword. I've always known it to exist for "good code" - design and readability.

如果我没记错我的java,那么使用final关键字来提高性能的方法就很少了。我一直都知道它的存在是因为“良好的代码”——设计和可读性。

#7


4  

I am amazed that no one has actually posted some real code that is de-compiled to prove that there is at least some minor difference.

我感到惊讶的是,实际上还没有人发布过一些经过反编译的真实代码,以证明至少存在一些细微的差异。

For the reference this has been tested against javac version 8, 9 and 10.

对于引用,已经对javac版本8、9和10进行了测试。

Suppose this method:

假设这个方法:

public static int test() {
    /* final */ Object left = new Object();
    Object right = new Object();

    return left.hashCode() + right.hashCode();
}

Compiling this code as it is, produces the exact same byte code as when final would have been present (final Object left = new Object();).

编译此代码时,生成的字节码与出现final时的字节码完全相同(final对象左侧= new Object();)。

But this one:

但是这一次:

public static int test() {
    /* final */ int left = 11;
    int right = 12;
    return left + right;
}

Produces:

生产:

   0: bipush        11
   2: istore_0
   3: bipush        12
   5: istore_1
   6: iload_0
   7: iload_1
   8: iadd
   9: ireturn

Leaving final to be present produces:

离开最终到现在产生:

   0: bipush        12
   2: istore_1
   3: bipush        11
   5: iload_1
   6: iadd
   7: ireturn

The code is pretty much self-explanatory, in case there is a compile time constant, it will be loaded directly onto the operand stack (it will not be stored into local variables array like the previous example does via bipush 12; istore_0; iload_0) - which sort of makes sense since no one can change it.

代码非常简单,如果有一个编译时常量,它将直接加载到操作数堆栈中(它不会像前面的示例通过bipush 12那样存储到本地变量数组中);istore_0;这是有意义的,因为没有人可以改变它。

On the other hand why in the second case the compiler does not produce istore_0 ... iload_0 is beyond me, it's not like that slot 0 is used in any way (it could shrink the variables array this way, but may be Im missing some internals details, can't tell for sure)

另一方面,为什么在第二种情况下,编译器不产生istore_0…iload_0超出了我的理解范围,它不会以任何方式使用插槽0(它可以以这种方式缩小变量数组,但可能我漏掉了一些内部细节,无法确定)

I was surprised to see such an optimization, considering how little ones javac does. As to should we always use final? I'm not even going to write a JMH test (which I wanted to initially), I am sure that the diff is in the order of ns (if possible to be captured at all). The only place this could be a problem, is when a method could not be inlined because of it's size (and declaring final would shrink that size by a few bytes).

考虑到javac所做的事情是如此之少,看到这样的优化我感到很惊讶。我们应该一直使用final吗?我甚至不打算写一个JMH测试(这是我最初想做的),我确信diff的顺序是ns(如果可能的话)。唯一可能出现问题的地方是,当一个方法不能被内联的时候,因为它的大小(并且声明final会将这个大小缩小几个字节)。

There are two more finals that need to be addressed. First is when a method is final (from a JIT perspective), such a method is monomorphic - and these are the most beloved ones by the JVM.

还有两场决赛需要解决。首先,当一个方法是最终的(从JIT的角度来看)时,这样的方法是单态的——这是JVM最喜欢的方法。

Then there are final instance variables (that must be set in every constructor); these are important as they will guarantee a correctly published reference, as touched a bit here and also specified exactly by the JLS.

然后还有最终的实例变量(必须在每个构造函数中设置);这些都很重要,因为它们将保证正确发布的引用,这里稍微涉及了一点,JLS也明确指定了这些引用。

#8


1  

I'm not an expert but I suppose you should add final keyword to the class or method if it won't be overwritten and leave variables alone. If there will be any way to optimize such things the compiler will do that for you.

我不是专家,但我认为您应该在类或方法中添加final关键字,如果它不被覆盖并且只留下变量。如果有什么方法可以优化这些东西,编译器会为你做这些。

#9


1  

Actually, while testing some OpenGL-related code, I found that using the final modifier on a private field can degrade performance. Here is the start of the class I tested:

实际上,在测试一些与opengl相关的代码时,我发现在私有字段上使用最终修饰符会降低性能。这是我测试的课程的开始:

public class ShaderInput {

    private /* final */ float[] input;
    private /* final */ int[] strides;


    public ShaderInput()
    {
        this.input = new float[10];
        this.strides = new int[] { 0, 4, 8 };
    }


    public ShaderInput x(int stride, float val)
    {
        input[strides[stride] + 0] = val;
        return this;
    }

    // more stuff ...

And this is the method I used to test the performance of various alternatives, amongst which the ShaderInput class:

这是我用来测试各种替代品性能的方法,其中ShaderInput类:

public static void test4()
{
    int arraySize = 10;
    float[] fb = new float[arraySize];
    for (int i = 0; i < arraySize; i++) {
        fb[i] = random.nextFloat();
    }
    int times = 1000000000;
    for (int i = 0; i < 10; ++i) {
        floatVectorTest(times, fb);
        arrayCopyTest(times, fb);
        shaderInputTest(times, fb);
        directFloatArrayTest(times, fb);
        System.out.println();
        System.gc();
    }
}

After the 3rd iteration, with the VM warmed up, I consistently got these figures without the final key word:

在第3次迭代之后,随着VM的预热,我始终得到这些数字,而没有最后一个关键词:

Simple array copy took   : 02.64
System.arrayCopy took    : 03.20
ShaderInput took         : 00.77
Unsafe float array took  : 05.47

With the final keyword:

最后的关键字:

Simple array copy took   : 02.66
System.arrayCopy took    : 03.20
ShaderInput took         : 02.59
Unsafe float array took  : 06.24

Note the figures for the ShaderInput test.

注意ShaderInput测试的图片。

It didn't matter whether I made the fields public or private.

不管我是公开的还是私有的。

Incidentally, there are a few more baffling things. The ShaderInput class outperforms all other variants, even with the final keyword. This is remarkable b/c it basically is a class wrapping a float array, while the other tests directly manipulate the array. Have to figure this one out. May have something to do with ShaderInput's fluent interface.

顺便说一句,还有一些令人困惑的事情。ShaderInput类比所有其他变体都要出色,即使使用final关键字。这是很了不起的b/c,它基本上是一个封装浮动数组的类,而其他测试直接操作数组。必须把这个算出来。可能与ShaderInput流畅的界面有关。

Also System.arrayCopy actually apparently is somewhat slower for small arrays than simply copying elements from one array to the other in a for loop. And using sun.misc.Unsafe (as well as direct java.nio.FloatBuffer, not shown here) performs abysmally.

同时系统。对于小数组来说,arrayCopy实际上比简单地在for循环中将元素从一个数组复制到另一个数组要慢一些。和使用sun.misc。不安全(以及直接java.nio)。FloatBuffer,这里没有显示)执行非常糟糕。

#10


0  

Definately yes, if variable converts as constant,

是的,如果变量转换为常数,

as we know java compiler convert such final variables as constant which possibles

正如我们所知道的,java编译器可以将最终变量转换为常量

as concept of constant java compiler directly replace the value with it's reference at compile time

作为常量java编译器的概念,它在编译时直接将值替换为它的引用

in java variables goes as constant if that is string or primitive type which given without any runtime process

在java中,如果是字符串或基元类型,则变量作为常量,并且没有任何运行时过程

otherwise it just final (non changeble) variable,

否则它就是最终的(不可改变的)变量,

& constatnt use is always faster than reference.

使用时总是比参考要快。

so if possible, use constants in any programming language for better performance

因此,如果可能的话,在任何编程语言中使用常量可以获得更好的性能

#11


-3  

final keyword can be used in five ways in Java.

在Java中,final关键字有五种用法。

  1. A class is final
  2. 一个类是终局的
  3. A reference variable is final
  4. 引用变量是最终的
  5. A local variable is final
  6. 局部变量是最终变量
  7. A method is final
  8. 一个方法是终局的

A class is final: a class is final means we cannot be extended or inheritance means inheritance is not possible.

类是final的:类是final的意思,意味着不能扩展或继承意味着不可能继承。

Similarly - A object is final: some time we does not modified the internal state of object so in such case we can specify the object is final object.object final means not variable also final.

类似地,对象是final:有时我们不修改对象的内部状态,因此在这种情况下,我们可以指定对象是final对象。对象最终意味着不变量也最终。

Once reference variable is made final, it cannot be reassigned to other object. But can change the contents of the object as long as its fields are not final

一旦引用变量成为最终变量,它就不能被重新分配给其他对象。但是只要对象的字段不是最终的,就可以修改对象的内容