0%

探究局部变量使用final会不会提升性能的问题

今天在和小伙伴codereview发现,平时使用idea自动生成变量名时一直默认在变量前加final,起初还以为这样会有什么性能的提升,所以idea默认加上。然后发现小伙伴们基本上都不怎么这样用。对于局部变量要不要加final产生了很大的争议。下面通过实操为大家揭开面纱。

网上的观点

网上对这个的争议也很大,但大都偏向:final修饰局部变量可以提高访问速度。
final观点
final观点

实操

考虑到fianl修饰的变量类型不同可能产生的效果不同,我们分别对基本类型和包装类型的局部变量进行验证

基本类型

1
2
3
4
5
6
7
8
9
10
11
12
13
//有final修饰
public int intFinalSum() {
final int a=34;
final int b=30;
return a+b;
}

//没有final修饰
public int intSum() {
int a=34;
int b=30;
return a+b;
}

包装类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//final修饰
public User getFinalUser() {
final User user = new User();
user.setAge(12);
user.setName("测试");
user.setId(new Random().nextInt());
return user;
}

//没有final修饰
public User getUser() {
User user = new User();
user.setAge(12);
user.setName("测试");
user.setId(new Random().nextInt());
return user;
}

下面对上述代买进行测试。

方式一:采用JMH进行耗时测试:

模拟用户请求,预热5次,总量10,统计单位微妙,分别对包装类型和基本类型进行测试。

1
2
3
4
5
Benchmark                          Mode   Samples         Mean   Mean error    Units
c.l.j.t.BasicType.getFinalUser avgt 10 0.081 0.024 us/op
c.l.j.t.BasicType.getUser avgt 10 0.068 0.004 us/op
c.l.j.t.BasicType.intFinalSum avgt 10 0.001 0.000 us/op
c.l.j.t.BasicType.intSum avgt 10 0.001 0.000 us/op

从上面结果来看消耗时间来看,基本类型耗时一致,包装类型差异不大。

考虑到测试的数据量有限,耗时暂作为参考。

方式二:编译后的字节码对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public com.xfyh.jmhtest.test.FinalTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

public int intFinalSum();
Code:
0: bipush 34
2: istore_1
3: bipush 30
5: istore_2
6: bipush 64
8: ireturn

public int intSum();
Code:
0: bipush 34
2: istore_1
3: bipush 30
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: ireturn

public com.xfyh.jmhtest.model.User getFinalUser();
Code:
0: new #2 // class com/xfyh/jmhtest/model/User
3: dup
4: invokespecial #3 // Method com/xfyh/jmhtest/model/User."<init>":()V
7: astore_1
8: aload_1
9: bipush 12
11: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: invokevirtual #5 // Method com/xfyh/jmhtest/model/User.setAge:(Ljava/lang/Integer;)V
17: aload_1
18: ldc #6 // String 测试
20: invokevirtual #7 // Method com/xfyh/jmhtest/model/User.setName:(Ljava/lang/String;)V
23: aload_1
24: new #8 // class java/util/Random
27: dup
28: invokespecial #9 // Method java/util/Random."<init>":()V
31: invokevirtual #10 // Method java/util/Random.nextInt:()I
34: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
37: invokevirtual #11 // Method com/xfyh/jmhtest/model/User.setId:(Ljava/lang/Integer;)V
40: aload_1
41: areturn

public com.xfyh.jmhtest.model.User getUser();
Code:
0: new #2 // class com/xfyh/jmhtest/model/User
3: dup
4: invokespecial #3 // Method com/xfyh/jmhtest/model/User."<init>":()V
7: astore_1
8: aload_1
9: bipush 12
11: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: invokevirtual #5 // Method com/xfyh/jmhtest/model/User.setAge:(Ljava/lang/Integer;)V
17: aload_1
18: ldc #6 // String 测试
20: invokevirtual #7 // Method com/xfyh/jmhtest/model/User.setName:(Ljava/lang/String;)V
23: aload_1
24: new #8 // class java/util/Random
27: dup
28: invokespecial #9 // Method java/util/Random."<init>":()V
31: invokevirtual #10 // Method java/util/Random.nextInt:()I
34: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
37: invokevirtual #11 // Method com/xfyh/jmhtest/model/User.setId:(Ljava/lang/Integer;)V
40: aload_1
41: areturn

从字节码上面分析,对于基本类型,final修饰的命令简短,所以判断final修饰的要快一些,包装类型字节码指令完全一样,所以加不加都一样。没差异。

结论

通过上面两个实验,我们可以看出,局部变量使用final会不会提升性能的问题,完全看个人编码习惯,都可以。