Java中final方法比非final性能更好?

无继承

有 static 修饰

static final

 
  1. // 生成随机数字和字母,

  2.    public static final String getStringRandomFinal(int length) {

  3.        String val = "";

  4.        Random random = new Random();

  5.        // 参数length,表示生成几位随机数

  6.        for (int i = 0; i < length; i++) {

  7.            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";

  8.            // 输出字母还是数字

  9.            if ("char".equalsIgnoreCase(charOrNum)) {

  10.                // 输出是大写字母还是小写字母

  11.                // int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;

  12.                val += (char) (random.nextInt(26) + 97);

  13.            } else if ("num".equalsIgnoreCase(charOrNum)) {

  14.                val += String.valueOf(random.nextInt(10));

  15.            }

  16.        }

  17.        return val;

  18.    }

static 非 final

 
  1. // 生成随机数字和字母,

  2.    public static String getStringRandom(int length) {

  3.        String val = "";

  4.        Random random = new Random();

  5.        // 参数length,表示生成几位随机数

  6.        for (int i = 0; i < length; i++) {

  7.            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";

  8.            // 输出字母还是数字

  9.            if ("char".equalsIgnoreCase(charOrNum)) {

  10.                // 输出是大写字母还是小写字母

  11.                // int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;

  12.                val += (char) (random.nextInt(26) + 97);

  13.            } else if ("num".equalsIgnoreCase(charOrNum)) {

  14.                val += String.valueOf(random.nextInt(10));

  15.            }

  16.        }

  17.        return val;

  18.    }

结果

这里使用了 OpenJDK 的 JMH 基准测试工具来测试的,结果如下:

 
  1. # JMH 1.4.1 (released 903 days ago, please consider updating!)

  2. # VM invoker: /srv/jdk1.8.0_92/jre/bin/java

  3. # VM options: <none>

  4. # Warmup: 20 iterations, 1 s each

  5. # Measurement: 20 iterations, 1 s each

  6. # Timeout: 10 min per iteration

  7. # Threads: 1 thread, will synchronize iterations

  8. # Benchmark mode: Throughput, ops/time

  9. # Benchmark: org.agoncal.sample.jmh.Main.benchmark

  10. 中间忽略了预热及测试过程,这里只显示结果

  11. Result: 206924.113 ±(99.9%) 7746.446 ops/s [Average]

  12.  Statistics: (min, avg, max) = (132107.466, 206924.113, 267265.397), stdev = 32798.937

  13.  Confidence interval (99.9%): [199177.667, 214670.559]

  14. # JMH 1.4.1 (released 903 days ago, please consider updating!)

  15. # VM invoker: /srv/jdk1.8.0_92/jre/bin/java

  16. # VM options: <none>

  17. # Warmup: 20 iterations, 1 s each

  18. # Measurement: 20 iterations, 1 s each

  19. # Timeout: 10 min per iteration

  20. # Threads: 1 thread, will synchronize iterations

  21. # Benchmark mode: Throughput, ops/time

  22. # Benchmark: org.agoncal.sample.jmh.Main.benchmarkFinal

  23. 中间忽略了预热及测试过程,这里只显示结果

  24. Result: 210111.568 ±(99.9%) 8486.176 ops/s [Average]

  25.  Statistics: (min, avg, max) = (133813.368, 210111.568, 267525.228), stdev = 35931.001

  26.  Confidence interval (99.9%): [201625.392, 218597.744]

  27. # Run complete. Total time: 00:13:54

  28. Benchmark                       Mode  Samples       Score      Error  Units

  29. o.a.s.j.Main.benchmark         thrpt      200  206924.113 ± 7746.446  ops/s

  30. o.a.s.j.Main.benchmarkFinal    thrpt      200  210111.568 ± 8486.176  ops/s

总结:你说final的性能比非final有没有提升呢?可以说有,但几乎可以忽略不计。如果单纯地追求性能,而将所有的方法修改为 final 的话,我认为这样子是不可取的。

而且这性能的差别,远远也没有网上有些人说的提升 50% 这么恐怖(有可能他们使用的是10年前的JVM来测试的吧^_^,比如 《35+ 个 Java 代码性能优化总结》这篇文章。雷总:不服?咱们来跑个分!)

分析

字节码级别的差别

StringKit.java StringKitFinal.java

它们在字节码上的差别:

 
  1. [18:52:08] emacsist:target $ diff /tmp/stringkit.log /tmp/stringkit-final.log

  2. 1,5c1,5

  3. < Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKit.class

  4. <   Last modified 2017-6-15; size 1098 bytes

  5. <   MD5 checksum fe1ccdde26107e4037afc54c780f2c95

  6. <   Compiled from "StringKit.java"

  7. < public class org.agoncal.sample.jmh.StringKit

  8. ---

  9. > Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKitFinal.class

  10. >   Last modified 2017-6-15; size 1118 bytes

  11. >   MD5 checksum 410f8bf0eb723b794e4754c6eb8b9829

  12. >   Compiled from "StringKitFinal.java"

  13. > public class org.agoncal.sample.jmh.StringKitFinal

  14. 24c24

  15. <   #15 = Class              #52            // org/agoncal/sample/jmh/StringKit

  16. ---

  17. >   #15 = Class              #52            // org/agoncal/sample/jmh/StringKitFinal

  18. 32,33c32,33

  19. <   #23 = Utf8               Lorg/agoncal/sample/jmh/StringKit;

  20. <   #24 = Utf8               getStringRandom

  21. ---

  22. >   #23 = Utf8               Lorg/agoncal/sample/jmh/StringKitFinal;

  23. >   #24 = Utf8               getStringRandomFinal

  24. 47c47

  25. <   #38 = Utf8               StringKit.java

  26. ---

  27. >   #38 = Utf8               StringKitFinal.java

  28. 61c61

  29. <   #52 = Utf8               org/agoncal/sample/jmh/StringKit

  30. ---

  31. >   #52 = Utf8               org/agoncal/sample/jmh/StringKitFinal

  32. 75c75

  33. <   public org.agoncal.sample.jmh.StringKit();

  34. ---

  35. >   public org.agoncal.sample.jmh.StringKitFinal();

  36. 87c87

  37. <             0       5     0  this   Lorg/agoncal/sample/jmh/StringKit;

  38. ---

  39. >             0       5     0  this   Lorg/agoncal/sample/jmh/StringKitFinal;

  40. 89c89

  41. <   public static java.lang.String getStringRandom(int);

  42. ---

  43. >   public static final java.lang.String getStringRandomFinal(int);

  44. 91c91

  45. <     flags: ACC_PUBLIC, ACC_STATIC

  46. ---

  47. >     flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL

  48. 187c187

  49. < SourceFile: "StringKit.java"

  50. ---

  51. > SourceFile: "StringKitFinal.java"

可以看到除了方法名和方法修饰符不同之外,其他的没有什么区别了。

在调用者上面的字节码差别

 
  1. public void benchmark();

  2.    descriptor: ()V

  3.    flags: ACC_PUBLIC

  4.    Code:

  5.      stack=1, locals=1, args_size=1

  6.         0: bipush        32

  7.         2: invokestatic  #2                  // Method org/agoncal/sample/jmh/StringKit.getStringRandom:(I)Ljava/lang/String;

  8.         5: pop

  9.         6: return

  10.      LineNumberTable:

  11.        line 21: 0

  12.        line 22: 6

  13.      LocalVariableTable:

  14.        Start  Length  Slot  Name   Signature

  15.            0       7     0  this   Lorg/agoncal/sample/jmh/Main;

  16.    RuntimeVisibleAnnotations:

  17.      0: #26()

  18.  public void benchmarkFinal();

  19.    descriptor: ()V

  20.    flags: ACC_PUBLIC

  21.    Code:

  22.      stack=1, locals=1, args_size=1

  23.         0: bipush        32

  24.         2: invokestatic  #3                  // Method org/agoncal/sample/jmh/StringKitFinal.getStringRandomFinal:(I)Ljava/lang/String;

  25.         5: pop

  26.         6: return

  27.      LineNumberTable:

  28.        line 26: 0

  29.        line 27: 6

  30.      LocalVariableTable:

  31.        Start  Length  Slot  Name   Signature

  32.            0       7     0  this   Lorg/agoncal/sample/jmh/Main;

  33.    RuntimeVisibleAnnotations:

  34.      0: #26()

可以看到,它们在调用者上面的字节码也没有什么区别,只是方法名不一样之外。

对于 JVM 来说,它是只认字节码的,既然字节码除了方法名和修饰符一样,其他都一样,那就可以大概推测它们的性能几乎可以忽略不计了。因为调用 static final 和 static 非 final 的JVM指令是一样。

无 static 修饰

方法体是一样的,只是将它们删除了 static 的修饰。

结果

 
  1. # JMH version: 1.19

  2. # VM version: JDK 1.8.0_92, VM 25.92-b14

  3. # VM invoker: /srv/jdk1.8.0_92/jre/bin/java

  4. # VM options: <none>

  5. # Warmup: 20 iterations, 1 s each

  6. # Measurement: 20 iterations, 1 s each

  7. # Timeout: 10 min per iteration

  8. # Threads: 1 thread, will synchronize iterations

  9. # Benchmark mode: Throughput, ops/time

  10. # Benchmark: org.agoncal.sample.jmh.Main.benchmark

  11. 中间忽略了预热及测试过程,这里只显示结果

  12. Result "org.agoncal.sample.jmh.Main.benchmark":

  13.  201306.770 ±(99.9%) 8184.423 ops/s [Average]

  14.  (min, avg, max) = (131889.934, 201306.770, 259928.172), stdev = 34653.361

  15.  CI (99.9%): [193122.347, 209491.193] (assumes normal distribution)

  16. # JMH version: 1.19

  17. # VM version: JDK 1.8.0_92, VM 25.92-b14

  18. # VM invoker: /srv/jdk1.8.0_92/jre/bin/java

  19. # VM options: <none>

  20. # Warmup: 20 iterations, 1 s each

  21. # Measurement: 20 iterations, 1 s each

  22. # Timeout: 10 min per iteration

  23. # Threads: 1 thread, will synchronize iterations

  24. # Benchmark mode: Throughput, ops/time

  25. # Benchmark: org.agoncal.sample.jmh.Main.benchmarkFinal

  26. 中间忽略了预热及测试过程,这里只显示结果

  27. Result "org.agoncal.sample.jmh.Main.benchmarkFinal":

  28.  196871.022 ±(99.9%) 8595.719 ops/s [Average]

  29.  (min, avg, max) = (131182.268, 196871.022, 265522.769), stdev = 36394.814

  30.  CI (99.9%): [188275.302, 205466.741] (assumes normal distribution)

  31. # Run complete. Total time: 00:13:35

  32. Benchmark             Mode  Cnt       Score      Error  Units

  33. Main.benchmark       thrpt  200  201306.770 ± 8184.423  ops/s

  34. Main.benchmarkFinal  thrpt  200  196871.022 ± 8595.719  ops/s

分析

字节码级别的差别

 
  1. [19:20:17] emacsist:target $ diff /tmp/stringkit.log /tmp/stringkit-final.log

  2. 1,5c1,5

  3. < Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKit.class

  4. <   Last modified 2017-6-15; size 1110 bytes

  5. <   MD5 checksum f61144e86f7c17dc5d5f2b2d35fac36d

  6. <   Compiled from "StringKit.java"

  7. < public class org.agoncal.sample.jmh.StringKit

  8. ---

  9. > Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKitFinal.class

  10. >   Last modified 2017-6-15; size 1130 bytes

  11. >   MD5 checksum 15ce17ee17fdb5f4721f0921977b1e69

  12. >   Compiled from "StringKitFinal.java"

  13. > public class org.agoncal.sample.jmh.StringKitFinal

  14. 24c24

  15. <   #15 = Class              #52            // org/agoncal/sample/jmh/StringKit

  16. ---

  17. >   #15 = Class              #52            // org/agoncal/sample/jmh/StringKitFinal

  18. 32,33c32,33

  19. <   #23 = Utf8               Lorg/agoncal/sample/jmh/StringKit;

  20. <   #24 = Utf8               getStringRandom

  21. ---

  22. >   #23 = Utf8               Lorg/agoncal/sample/jmh/StringKitFinal;

  23. >   #24 = Utf8               getStringRandomFinal

  24. 47c47

  25. <   #38 = Utf8               StringKit.java

  26. ---

  27. >   #38 = Utf8               StringKitFinal.java

  28. 61c61

  29. <   #52 = Utf8               org/agoncal/sample/jmh/StringKit

  30. ---

  31. >   #52 = Utf8               org/agoncal/sample/jmh/StringKitFinal

  32. 75c75

  33. <   public org.agoncal.sample.jmh.StringKit();

  34. ---

  35. >   public org.agoncal.sample.jmh.StringKitFinal();

  36. 87c87

  37. <             0       5     0  this   Lorg/agoncal/sample/jmh/StringKit;

  38. ---

  39. >             0       5     0  this   Lorg/agoncal/sample/jmh/StringKitFinal;

  40. 89c89

  41. <   public java.lang.String getStringRandom(int);

  42. ---

  43. >   public final java.lang.String getStringRandomFinal(int);

  44. 91c91

  45. <     flags: ACC_PUBLIC

  46. ---

  47. >     flags: ACC_PUBLIC, ACC_FINAL

  48. 169c169

  49. <             0     125     0  this   Lorg/agoncal/sample/jmh/StringKit;

  50. ---

  51. >             0     125     0  this   Lorg/agoncal/sample/jmh/StringKitFinal;

  52. 188c188

  53. < SourceFile: "StringKit.java"

  54. ---

  55. > SourceFile: "StringKitFinal.java"

可以看到,字节码上除了名字和 final 修饰符差别外,其余的是一样的。

在调用者上面的字节码差别

 
  1. public void benchmark();

  2.    descriptor: ()V

  3.    flags: ACC_PUBLIC

  4.    Code:

  5.      stack=2, locals=1, args_size=1

  6.         0: new           #2                  // class org/agoncal/sample/jmh/StringKit

  7.         3: dup

  8.         4: invokespecial #3                  // Method org/agoncal/sample/jmh/StringKit."<init>":()V

  9.         7: bipush        32

  10.         9: invokevirtual #4                  // Method org/agoncal/sample/jmh/StringKit.getStringRandom:(I)Ljava/lang/String;

  11.        12: pop

  12.        13: return

  13.      LineNumberTable:

  14.        line 21: 0

  15.        line 22: 13

  16.      LocalVariableTable:

  17.        Start  Length  Slot  Name   Signature

  18.            0      14     0  this   Lorg/agoncal/sample/jmh/Main;

  19.    RuntimeVisibleAnnotations:

  20.      0: #30()

  21.  public void benchmarkFinal();

  22.    descriptor: ()V

  23.    flags: ACC_PUBLIC

  24.    Code:

  25.      stack=2, locals=1, args_size=1

  26.         0: new           #5                  // class org/agoncal/sample/jmh/StringKitFinal

  27.         3: dup

  28.         4: invokespecial #6                  // Method org/agoncal/sample/jmh/StringKitFinal."<init>":()V

  29.         7: bipush        32

  30.         9: invokevirtual #7                  // Method org/agoncal/sample/jmh/StringKitFinal.getStringRandomFinal:(I)Ljava/lang/String;

  31.        12: pop

  32.        13: return

  33.      LineNumberTable:

  34.        line 26: 0

  35.        line 27: 13

  36.      LocalVariableTable:

  37.        Start  Length  Slot  Name   Signature

  38.            0      14     0  this   Lorg/agoncal/sample/jmh/Main;

  39.    RuntimeVisibleAnnotations:

  40.      0: #30()

可以看到,它们除了名字不同之外,其他的JVM指令都是一样的。

总结

对于是否有 final 修饰的方法,对性能的影响可以忽略不计。因为它们生成的字节码除了 flags 标志位是否有 final 修饰不同之外,其他所有的JVM指令,都是一样的(对于方法本身,以及调用者本身的字节码都一样)。对于JVM来说,它执行的就是字节码,如果字节码都一样的话,那对于JVM来说,它就是同一样东西的了。

有继承

无 final 修饰

 
  1. package org.agoncal.sample.jmh;

  2. import java.util.Random;

  3. /**

  4. * Created by emacsist on 2017/6/15.

  5. */

  6. public abstract class StringKitAbs {

  7.    // 生成随机数字和字母,

  8.    public String getStringRandom(int length) {

  9.        String val = "";

  10.        Random random = new Random();

  11.        // 参数length,表示生成几位随机数

  12.        for (int i = 0; i < length; i++) {

  13.            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";

  14.            // 输出字母还是数字

  15.            if ("char".equalsIgnoreCase(charOrNum)) {

  16.                // 输出是大写字母还是小写字母

  17.                // int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;

  18.                val += (char) (random.nextInt(26) + 97);

  19.            } else if ("num".equalsIgnoreCase(charOrNum)) {

  20.                val += String.valueOf(random.nextInt(10));

  21.            }

  22.        }

  23.        return val;

  24.    }

  25. }

有 final 修饰

 
  1. package org.agoncal.sample.jmh;

  2. import java.util.Random;

  3. /**

  4. * Created by emacsist on 2017/6/15.

  5. */

  6. public abstract class StringKitAbsFinal {

  7.    // 生成随机数字和字母,

  8.    public final String getStringRandomFinal(int length) {

  9.        String val = "";

  10.        Random random = new Random();

  11.        // 参数length,表示生成几位随机数

  12.        for (int i = 0; i < length; i++) {

  13.            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";

  14.            // 输出字母还是数字

  15.            if ("char".equalsIgnoreCase(charOrNum)) {

  16.                // 输出是大写字母还是小写字母

  17.                // int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;

  18.                val += (char) (random.nextInt(26) + 97);

  19.            } else if ("num".equalsIgnoreCase(charOrNum)) {

  20.                val += String.valueOf(random.nextInt(10));

  21.            }

  22.        }

  23.        return val;

  24.    }

  25. }

测试代码

写一个类来继承上面的抽象类,以此来测试在继承中 final 有否对多态中的影响

 
  1. package org.agoncal.sample.jmh;

  2. /**

  3. * Created by emacsist on 2017/6/15.

  4. */

  5. public class StringKitFinal extends StringKitAbsFinal {

  6. }

 

 
  1. package org.agoncal.sample.jmh;

  2. /**

  3. * Created by emacsist on 2017/6/15.

  4. */

  5. public class StringKit extends StringKitAbs {

  6. }

然后在基准测试中:

 
  1. @Benchmark

  2.    public void benchmark() {

  3.        new StringKit().getStringRandom(32);

  4.    }

  5.    @Benchmark

  6.    public void benchmarkFinal() {

  7.        new StringKitFinal().getStringRandomFinal(32);

  8.    }

测试结果

非 final 结果

 
  1. # JMH version: 1.19

  2. # VM version: JDK 1.8.0_92, VM 25.92-b14

  3. # VM invoker: /srv/jdk1.8.0_92/jre/bin/java

  4. # VM options: <none>

  5. # Warmup: 20 iterations, 1 s each

  6. # Measurement: 20 iterations, 1 s each

  7. # Timeout: 10 min per iteration

  8. # Threads: 1 thread, will synchronize iterations

  9. # Benchmark mode: Throughput, ops/time

  10. # Benchmark: org.agoncal.sample.jmh.Main.benchmark

  11. 中间忽略了预热及测试过程

  12. Result "org.agoncal.sample.jmh.Main.benchmark":

  13.  213462.677 ±(99.9%) 8670.164 ops/s [Average]

  14.  (min, avg, max) = (135751.428, 213462.677, 264182.887), stdev = 36710.017

  15.  CI (99.9%): [204792.513, 222132.841] (assumes normal distribution)

有 final 结果

 
  1. # JMH version: 1.19

  2. # VM version: JDK 1.8.0_92, VM 25.92-b14

  3. # VM invoker: /srv/jdk1.8.0_92/jre/bin/java

  4. # VM options: <none>

  5. # Warmup: 20 iterations, 1 s each

  6. # Measurement: 20 iterations, 1 s each

  7. # Timeout: 10 min per iteration

  8. # Threads: 1 thread, will synchronize iterations

  9. # Benchmark mode: Throughput, ops/time

  10. # Benchmark: org.agoncal.sample.jmh.Main.benchmarkFinal

  11. 中间忽略了预热及测试过程

  12. Result "org.agoncal.sample.jmh.Main.benchmarkFinal":

  13.  213684.585 ±(99.9%) 8571.512 ops/s [Average]

  14.  (min, avg, max) = (133472.162, 213684.585, 267742.236), stdev = 36292.318

  15.  CI (99.9%): [205113.073, 222256.097] (assumes normal distribution)

总对比

 
  1. # Run complete. Total time: 00:13:35

  2. Benchmark             Mode  Cnt       Score      Error  Units

  3. Main.benchmark       thrpt  200  213462.677 ± 8670.164  ops/s

  4. Main.benchmarkFinal  thrpt  200  213684.585 ± 8571.512  ops/s

它们字节码的区别

 
  1. [12:12:19] emacsist:classes $ diff /tmp/StringKit.log /tmp/StringKitFinal.log

  2. 1,5c1,5

  3. < Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKit.class

  4. <   Last modified 2017-6-16; size 317 bytes

  5. <   MD5 checksum 7f9b024adc7f39345215e3e8490cafe4

  6. <   Compiled from "StringKit.java"

  7. < public class org.agoncal.sample.jmh.StringKit extends org.agoncal.sample.jmh.StringKitAbs

  8. ---

  9. > Classfile /Users/emacsist/Documents/idea/logging/target/classes/org/agoncal/sample/jmh/StringKitFinal.class

  10. >   Last modified 2017-6-16; size 337 bytes

  11. >   MD5 checksum f54eadc79a90675d97e95f766ef88a87

  12. >   Compiled from "StringKitFinal.java"

  13. > public class org.agoncal.sample.jmh.StringKitFinal extends org.agoncal.sample.jmh.StringKitAbsFinal

  14. 10,12c10,12

  15. <    #1 = Methodref          #3.#13         // org/agoncal/sample/jmh/StringKitAbs."<init>":()V

  16. <    #2 = Class              #14            // org/agoncal/sample/jmh/StringKit

  17. <    #3 = Class              #15            // org/agoncal/sample/jmh/StringKitAbs

  18. ---

  19. >    #1 = Methodref          #3.#13         // org/agoncal/sample/jmh/StringKitAbsFinal."<init>":()V

  20. >    #2 = Class              #14            // org/agoncal/sample/jmh/StringKitFinal

  21. >    #3 = Class              #15            // org/agoncal/sample/jmh/StringKitAbsFinal

  22. 19c19

  23. <   #10 = Utf8               Lorg/agoncal/sample/jmh/StringKit;

  24. ---

  25. >   #10 = Utf8               Lorg/agoncal/sample/jmh/StringKitFinal;

  26. 21c21

  27. <   #12 = Utf8               StringKit.java

  28. ---

  29. >   #12 = Utf8               StringKitFinal.java

  30. 23,24c23,24

  31. <   #14 = Utf8               org/agoncal/sample/jmh/StringKit

  32. <   #15 = Utf8               org/agoncal/sample/jmh/StringKitAbs

  33. ---

  34. >   #14 = Utf8               org/agoncal/sample/jmh/StringKitFinal

  35. >   #15 = Utf8               org/agoncal/sample/jmh/StringKitAbsFinal

  36. 26c26

  37. <   public org.agoncal.sample.jmh.StringKit();

  38. ---

  39. >   public org.agoncal.sample.jmh.StringKitFinal();

  40. 32c32

  41. <          1: invokespecial #1                  // Method org/agoncal/sample/jmh/StringKitAbs."<init>":()V

  42. ---

  43. >          1: invokespecial #1                  // Method org/agoncal/sample/jmh/StringKitAbsFinal."<init>":()V

  44. 38c38

  45. <             0       5     0  this   Lorg/agoncal/sample/jmh/StringKit;

  46. ---

  47. >             0       5     0  this   Lorg/agoncal/sample/jmh/StringKitFinal;

  48. 40c40

  49. < SourceFile: "StringKit.java"

  50. ---

  51. > SourceFile: "StringKitFinal.java"

可以看到,除了它们的方法签名和方法名字不同之外其他的都是一样的,包括JVM调用指令也完全是一样的。

结论

可以看到它们几乎是一样的。

总结

基于上面的基准测试结论,我认为滥用或刻意为了所谓的提升性能,而去为每一个方法尽可能添加 final 的关键字是不可取的。使用 final ,更多的应该是根据Java对 final 的语义来定义,而不是只想着为了提升性能(而且这影响可以忽略不计)而刻意用 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值