这里使用了 OpenJDK 的 JMH 基准测试工具来测试的,结果如下: 总结:你说final的性能比非final有没有提升呢?可以说有,但几乎可以忽略不计。如果单纯地追求性能,而将所有的方法修改为 final 的话,我认为这样子是不可取的。 而且这性能的差别,远远也没有网上有些人说的提升 50% 这么恐怖(有可能他们使用的是10年前的JVM来测试的吧^_^,比如 《35+ 个 Java 代码性能优化总结》这篇文章。雷总:不服?咱们来跑个分!) 它们在字节码上的差别: 可以看到除了方法名和方法修饰符不同之外,其他的没有什么区别了。 可以看到,它们在调用者上面的字节码也没有什么区别,只是方法名不一样之外。 对于 JVM 来说,它是只认字节码的,既然字节码除了方法名和修饰符一样,其他都一样,那就可以大概推测它们的性能几乎可以忽略不计了。因为调用 static final 和 static 非 final 的JVM指令是一样。 方法体是一样的,只是将它们删除了 static 的修饰。 可以看到,字节码上除了名字和 final 修饰符差别外,其余的是一样的。 可以看到,它们除了名字不同之外,其他的JVM指令都是一样的。 对于是否有 final 修饰的方法,对性能的影响可以忽略不计。因为它们生成的字节码除了 flags 标志位是否有 final 修饰不同之外,其他所有的JVM指令,都是一样的(对于方法本身,以及调用者本身的字节码都一样)。对于JVM来说,它执行的就是字节码,如果字节码都一样的话,那对于JVM来说,它就是同一样东西的了。 写一个类来继承上面的抽象类,以此来测试在继承中 final 有否对多态中的影响 然后在基准测试中: 有 final 结果 可以看到,除了它们的方法签名和方法名字不同之外其他的都是一样的,包括JVM调用指令也完全是一样的。 可以看到它们几乎是一样的。 基于上面的基准测试结论,我认为滥用或刻意为了所谓的提升性能,而去为每一个方法尽可能添加 final 的关键字是不可取的。使用 final ,更多的应该是根据Java对 final 的语义来定义,而不是只想着为了提升性能(而且这影响可以忽略不计)而刻意用 final. final 变量: 表示只读(只初始化一次,但可多次读取) final 方法:表示子类不可以重写。(网上认为 final 比非 final 快,就是认为它是在编译的时候已经静态绑定了,不需要在运行时再动态绑定。这个可能以前的JVM上是正确的,但在现代的JVM上,这个可以认为没什么影响,至少我在基准测试里是这样子) final 类:它们不能被继承,而且final类的方法,默认也是 final 的。 关于这个 final 的性能问题,我也Google了下,发现 stackoverflow 上,也有类似的问题: https://www.javazhiyin.com/go?url=https://stackoverflow.com/questions/4279420/does-use-of-final-keyword-in-java-improve-the-performance End 扩展阅读 淘宝服务端高并发分布式架构演进之路 Java中关于try、catch、finally中的细节分析 7大绝招帮你轻轻松松提升MySQL性能 Spring注解@Resource和@Autowired区别对比 为什么说Redis是单线程的? 作者:emacsist 来源:https://emacsist.github.io/// 生成随机数字和字母, public static final String getStringRandomFinal(int length) { String val = ""; Random random = new Random(); // 参数length,表示生成几位随机数 for (int i = 0; i
static 非 final
// 生成随机数字和字母, public static String getStringRandom(int length) { String val = ""; Random random = new Random(); // 参数length,表示生成几位随机数 for (int i = 0; i
结果
# JMH 1.4.1 (released 903 days ago, please consider updating!)
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options:
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmark
中间忽略了预热及测试过程,这里只显示结果
Result: 206924.113 ±(99.9%) 7746.446 ops/s [Average] Statistics: (min, avg, max) = (132107.466, 206924.113, 267265.397), stdev = 32798.937 Confidence interval (99.9%): [199177.667, 214670.559]
# JMH 1.4.1 (released 903 days ago, please consider updating!)
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options:
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmarkFinal
中间忽略了预热及测试过程,这里只显示结果
Result: 210111.568 ±(99.9%) 8486.176 ops/s [Average] Statistics: (min, avg, max) = (133813.368, 210111.568, 267525.228), stdev = 35931.001 Confidence interval (99.9%): [201625.392, 218597.744]
# Run complete. Total time: 00:13:54
Benchmark Mode Samples Score Error Units
o.a.s.j.Main.benchmark thrpt 200 206924.113 ± 7746.446 ops/s
o.a.s.j.Main.benchmarkFinal thrpt 200 210111.568 ± 8486.176 ops/s分析
字节码级别的差别
StringKit.java StringKitFinal.java
[18:52:08] emacsist:target $ diff /tmp/stringkit.log /tmp/stringkit-final.log
1,5c1,5
< MD5 checksum fe1ccdde26107e4037afc54c780f2c95
< Compiled from "StringKit.java"
> Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKitFinal.class
> Last modified 2017-6-15; size 1118 bytes
> MD5 checksum 410f8bf0eb723b794e4754c6eb8b9829
> Compiled from "StringKitFinal.java"
> public class org.agoncal.sample.jmh.StringKitFinal
24c24
< #15 &#61; Class #52 // org/agoncal/sample/jmh/StringKit
---
> #15 &#61; Class #52 // org/agoncal/sample/jmh/StringKitFinal
32,33c32,33
< #23 &#61; Utf8 Lorg/agoncal/sample/jmh/StringKit;
< #24 &#61; Utf8 getStringRandom
---
> #23 &#61; Utf8 Lorg/agoncal/sample/jmh/StringKitFinal;
> #24 &#61; Utf8 getStringRandomFinal
47c47
< #38 &#61; Utf8 StringKit.java
---
> #38 &#61; Utf8 StringKitFinal.java
61c61
< #52 &#61; Utf8 org/agoncal/sample/jmh/StringKit
---
> #52 &#61; Utf8 org/agoncal/sample/jmh/StringKitFinal
75c75
< public org.agoncal.sample.jmh.StringKit();
---
> public org.agoncal.sample.jmh.StringKitFinal();
87c87
< 0 5 0 this Lorg/agoncal/sample/jmh/StringKit;
---
> 0 5 0 this Lorg/agoncal/sample/jmh/StringKitFinal;
89c89
< public static java.lang.String getStringRandom(int);
---
> public static final java.lang.String getStringRandomFinal(int);
91c91
< flags: ACC_PUBLIC, ACC_STATIC
---
> flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
187c187
> SourceFile: "StringKitFinal.java"在调用者上面的字节码差别
public void benchmark(); descriptor: ()V flags: ACC_PUBLIC Code: stack&#61;1, locals&#61;1, args_size&#61;1 0: bipush 32 2: invokestatic #2 // Method org/agoncal/sample/jmh/StringKit.getStringRandom:(I)Ljava/lang/String; 5: pop 6: return LineNumberTable: line 21: 0 line 22: 6 LocalVariableTable: Start Length Slot Name Signature 0 7 0 this Lorg/agoncal/sample/jmh/Main; RuntimeVisibleAnnotations: 0: #26() public void benchmarkFinal(); descriptor: ()V flags: ACC_PUBLIC Code: stack&#61;1, locals&#61;1, args_size&#61;1 0: bipush 32 2: invokestatic #3 // Method org/agoncal/sample/jmh/StringKitFinal.getStringRandomFinal:(I)Ljava/lang/String; 5: pop 6: return LineNumberTable: line 26: 0 line 27: 6 LocalVariableTable: Start Length Slot Name Signature 0 7 0 this Lorg/agoncal/sample/jmh/Main; RuntimeVisibleAnnotations: 0: #26()
无 static 修饰
结果
# JMH version: 1.19
# VM version: JDK 1.8.0_92, VM 25.92-b14
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options:
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmark
中间忽略了预热及测试过程&#xff0c;这里只显示结果
Result "org.agoncal.sample.jmh.Main.benchmark": 201306.770 ±(99.9%) 8184.423 ops/s [Average] (min, avg, max) &#61; (131889.934, 201306.770, 259928.172), stdev &#61; 34653.361 CI (99.9%): [193122.347, 209491.193] (assumes normal distribution)
# JMH version: 1.19
# VM version: JDK 1.8.0_92, VM 25.92-b14
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options:
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmarkFinal
中间忽略了预热及测试过程&#xff0c;这里只显示结果
Result "org.agoncal.sample.jmh.Main.benchmarkFinal": 196871.022 ±(99.9%) 8595.719 ops/s [Average] (min, avg, max) &#61; (131182.268, 196871.022, 265522.769), stdev &#61; 36394.814 CI (99.9%): [188275.302, 205466.741] (assumes normal distribution)
# Run complete. Total time: 00:13:35
Benchmark Mode Cnt Score Error Units
Main.benchmark thrpt 200 201306.770 ± 8184.423 ops/s
Main.benchmarkFinal thrpt 200 196871.022 ± 8595.719 ops/s分析
字节码级别的差别
[19:20:17] emacsist:target $ diff /tmp/stringkit.log /tmp/stringkit-final.log
1,5c1,5
< MD5 checksum f61144e86f7c17dc5d5f2b2d35fac36d
< Compiled from "StringKit.java"
> Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKitFinal.class
> Last modified 2017-6-15; size 1130 bytes
> MD5 checksum 15ce17ee17fdb5f4721f0921977b1e69
> Compiled from "StringKitFinal.java"
> public class org.agoncal.sample.jmh.StringKitFinal
24c24
< #15 &#61; Class #52 // org/agoncal/sample/jmh/StringKit
---
> #15 &#61; Class #52 // org/agoncal/sample/jmh/StringKitFinal
32,33c32,33
< #23 &#61; Utf8 Lorg/agoncal/sample/jmh/StringKit;
< #24 &#61; Utf8 getStringRandom
---
> #23 &#61; Utf8 Lorg/agoncal/sample/jmh/StringKitFinal;
> #24 &#61; Utf8 getStringRandomFinal
47c47
< #38 &#61; Utf8 StringKit.java
---
> #38 &#61; Utf8 StringKitFinal.java
61c61
< #52 &#61; Utf8 org/agoncal/sample/jmh/StringKit
---
> #52 &#61; Utf8 org/agoncal/sample/jmh/StringKitFinal
75c75
< public org.agoncal.sample.jmh.StringKit();
---
> public org.agoncal.sample.jmh.StringKitFinal();
87c87
< 0 5 0 this Lorg/agoncal/sample/jmh/StringKit;
---
> 0 5 0 this Lorg/agoncal/sample/jmh/StringKitFinal;
89c89
< public java.lang.String getStringRandom(int);
---
> public final java.lang.String getStringRandomFinal(int);
91c91
< flags: ACC_PUBLIC
---
> flags: ACC_PUBLIC, ACC_FINAL
169c169
< 0 125 0 this Lorg/agoncal/sample/jmh/StringKit;
---
> 0 125 0 this Lorg/agoncal/sample/jmh/StringKitFinal;
188c188
> SourceFile: "StringKitFinal.java"在调用者上面的字节码差别
public void benchmark(); descriptor: ()V flags: ACC_PUBLIC Code: stack&#61;2, locals&#61;1, args_size&#61;1 0: new #2 // class org/agoncal/sample/jmh/StringKit 3: dup 4: invokespecial #3 // Method org/agoncal/sample/jmh/StringKit."
总结
有继承
无 final 修饰
package org.agoncal.sample.jmh;
import java.util.Random;
/** * Created by emacsist on 2017/6/15. */
public abstract class StringKitAbs { // 生成随机数字和字母, public String getStringRandom(int length) { String val &#61; ""; Random random &#61; new Random(); // 参数length&#xff0c;表示生成几位随机数 for (int i &#61; 0; i 有 final 修饰
package org.agoncal.sample.jmh;
import java.util.Random;
/** * Created by emacsist on 2017/6/15. */
public abstract class StringKitAbsFinal { // 生成随机数字和字母, public final String getStringRandomFinal(int length) { String val &#61; ""; Random random &#61; new Random(); // 参数length&#xff0c;表示生成几位随机数 for (int i &#61; 0; i 测试代码
package org.agoncal.sample.jmh;
/** * Created by emacsist on 2017/6/15. */
public class StringKitFinal extends StringKitAbsFinal {
}package org.agoncal.sample.jmh;
/** * Created by emacsist on 2017/6/15. */
public class StringKit extends StringKitAbs {
}&#64;Benchmark public void benchmark() { new StringKit().getStringRandom(32); } &#64;Benchmark public void benchmarkFinal() { new StringKitFinal().getStringRandomFinal(32); }
测试结果
非 final 结果
# JMH version: 1.19
# VM version: JDK 1.8.0_92, VM 25.92-b14
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options:
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmark
中间忽略了预热及测试过程
Result "org.agoncal.sample.jmh.Main.benchmark": 213462.677 ±(99.9%) 8670.164 ops/s [Average] (min, avg, max) &#61; (135751.428, 213462.677, 264182.887), stdev &#61; 36710.017 CI (99.9%): [204792.513, 222132.841] (assumes normal distribution)# JMH version: 1.19
# VM version: JDK 1.8.0_92, VM 25.92-b14
# VM invoker: /srv/jdk1.8.0_92/jre/bin/java
# VM options:
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.agoncal.sample.jmh.Main.benchmarkFinal
中间忽略了预热及测试过程
Result "org.agoncal.sample.jmh.Main.benchmarkFinal": 213684.585 ±(99.9%) 8571.512 ops/s [Average] (min, avg, max) &#61; (133472.162, 213684.585, 267742.236), stdev &#61; 36292.318 CI (99.9%): [205113.073, 222256.097] (assumes normal distribution)总对比
# Run complete. Total time: 00:13:35
Benchmark Mode Cnt Score Error Units
Main.benchmark thrpt 200 213462.677 ± 8670.164 ops/s
Main.benchmarkFinal thrpt 200 213684.585 ± 8571.512 ops/s它们字节码的区别
[12:12:19] emacsist:classes $ diff /tmp/StringKit.log /tmp/StringKitFinal.log
1,5c1,5
< MD5 checksum 7f9b024adc7f39345215e3e8490cafe4
< Compiled from "StringKit.java"
> Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKitFinal.class
> Last modified 2017-6-16; size 337 bytes
> MD5 checksum f54eadc79a90675d97e95f766ef88a87
> Compiled from "StringKitFinal.java"
> public class org.agoncal.sample.jmh.StringKitFinal extends org.agoncal.sample.jmh.StringKitAbsFinal
10,12c10,12
< #1 &#61; Methodref #3.#13 // org/agoncal/sample/jmh/StringKitAbs."
< #2 &#61; Class #14 // org/agoncal/sample/jmh/StringKit
< #3 &#61; Class #15 // org/agoncal/sample/jmh/StringKitAbs
---
> #1 &#61; Methodref #3.#13 // org/agoncal/sample/jmh/StringKitAbsFinal."
> #2 &#61; Class #14 // org/agoncal/sample/jmh/StringKitFinal
> #3 &#61; Class #15 // org/agoncal/sample/jmh/StringKitAbsFinal
19c19
< #10 &#61; Utf8 Lorg/agoncal/sample/jmh/StringKit;
---
> #10 &#61; Utf8 Lorg/agoncal/sample/jmh/StringKitFinal;
21c21
< #12 &#61; Utf8 StringKit.java
---
> #12 &#61; Utf8 StringKitFinal.java
23,24c23,24
< #14 &#61; Utf8 org/agoncal/sample/jmh/StringKit
< #15 &#61; Utf8 org/agoncal/sample/jmh/StringKitAbs
---
> #14 &#61; Utf8 org/agoncal/sample/jmh/StringKitFinal
> #15 &#61; Utf8 org/agoncal/sample/jmh/StringKitAbsFinal
26c26
< public org.agoncal.sample.jmh.StringKit();
---
> public org.agoncal.sample.jmh.StringKitFinal();
32c32
< 1: invokespecial #1 // Method org/agoncal/sample/jmh/StringKitAbs."
---
> 1: invokespecial #1 // Method org/agoncal/sample/jmh/StringKitAbsFinal."
38c38
< 0 5 0 this Lorg/agoncal/sample/jmh/StringKit;
---
> 0 5 0 this Lorg/agoncal/sample/jmh/StringKitFinal;
40c40
> SourceFile: "StringKitFinal.java"结论
总结
使用 final 的情况:
参考资料