在 Java 中使用 final 关键字会提高性能吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4279420/
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
Does use of final keyword in Java improve the performance?
提问by Abhishek Jain
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 程序员应该养成什么习惯来充分利用关键字?
采纳答案by Jon Skeet
Usually not. For virtual methods, HotSpot keeps track of whether the method has actuallybeen overridden, and is able to perform optimizations such as inlining on the assumptionthat 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.
通常不会。对于虚方法,HotSpot 会跟踪该方法是否实际上已被覆盖,并且能够执行优化,例如在方法尚未被覆盖的假设下进行内联- 直到它加载一个覆盖该方法的类,此时它可以撤消(或部分撤消)这些优化。
(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 字段,值得一提的是,无论如何,就清晰的设计而言,它们通常是一个好主意。它们还改变了跨线程可见性方面的保证行为:在构造函数完成后,任何 final 字段都保证立即在其他线程中可见。根据final
我的经验,这可能是最常见的用法,尽管作为 Josh Bloch 的“设计继承或禁止继承”经验法则的支持者,我可能应该final
更频繁地将其用于类......
回答by wmitchell
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
回答by Neowizard
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 关键字几乎没有办法提高性能。我一直都知道它的存在是为了“好的代码”——设计和可读性。
回答by sleske
You are really asking about two (at least) different cases:
您实际上是在询问两种(至少)不同的情况:
final
for local variablesfinal
for methods/classes
final
对于局部变量final
对于方法/类
Jon Skeet has already answered 2). About 1):
Jon Skeet 已经回答了 2)。关于 1):
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
。
final
mightmake 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
canimprove performance for class fields in some cases. I stand corrected.
实际上,根据 Timo Westk?mper 的回答,在某些情况下final
可以提高类字段的性能。我站着纠正。
回答by Crozin
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
在类或方法中添加关键字,如果它不会被覆盖并且不影响变量。如果有任何方法可以优化这些事情,编译器会为你做。
回答by adityaraj
final
keyword can be used in five ways in Java.
final
关键字在 Java 中有五种使用方式。
- A class is final
- A reference variable is final
- A local variable is final
- A method is final
- 一堂课是final
- 引用变量是最终的
- 局部变量是最终的
- 方法是最终的
A class is final: a class is final means we cannot be extended or inheritance means inheritance is not possible.
一个类是最终的:一个类是最终的意味着我们不能被扩展或者继承意味着继承是不可能的。
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.
类似地 - 对象是最终的:有时我们不会修改对象的内部状态,因此在这种情况下,我们可以指定对象是最终对象。对象最终意味着不变量也是最终的。
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
一旦引用变量成为最终的,它就不能重新分配给其他对象。但是可以改变对象的内容,只要它的字段不是最终的
回答by mel3kings
YES it can. Here is an instance where final can boost performance:
是的,它可以。这是一个 final 可以提高性能的实例:
Conditional compilationis 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 somethinglike this:
通过将 doSomething 属性转换为 final 属性,您已经告诉编译器无论何时看到 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 编译这段代码并用它反编译时,javap -c Test.class
我们得到:
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
。这证明最终变量对性能有影响,至少对于这个简单的情况是这样。
回答by rustyx
Short answer: don't worry about it!
简短的回答:别担心!
Long answer:
长答案:
When talking about final local variableskeep 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 局部变量时,请记住,使用关键字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 mightbe 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
对性能有任何可衡量的影响。
回答by user3663845
Actually, while testing some OpenGL-related code, I found that using the final modifier on a private field can degradeperformance. Here is the start of the class I tested:
实际上,在测试一些与 OpenGL 相关的代码时,我发现在私有字段上使用 final 修饰符会降低性能。这是我测试的课程的开始:
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 withoutthe 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
Withthe final keyword:
使用final 关键字:
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 directlymanipulate 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.
此外 System.arrayCopy 实际上对于小数组来说显然比在 for 循环中简单地将元素从一个数组复制到另一个数组要慢一些。并且使用 sun.misc.Unsafe(以及直接的 java.nio.FloatBuffer,这里没有显示)表现非常糟糕。
回答by Eugene
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 exactsame byte code as when final
would have been present (final Object left = new Object();
).
按原样编译此代码会产生与出现时完全相同的字节码final
( final Object left = 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:
离开final
会产生:
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; iload_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
测试(我最初想这样做),我确信差异的顺序是ns
(如果可能的话,完全可以捕获)。这可能是一个问题的唯一地方,是当一个方法由于它的大小而不能被内联时(并且声明final
会将该大小缩小几个字节)。
There are two more final
s 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 belovedones by the JVM
.
还有两个final
s 需要解决。第一种是当一个方法是final
(从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 hereand also specified exactly by the JLS
.
然后是final
实例变量(必须在每个构造函数中设置);这些很重要,因为它们将保证正确发布的参考,正如这里所触及的,也由JLS
.