局部变量为什么快?
要理解为什么局部变量会比全局变量快这个问题,我们只需要使用 javac
把他们编译成字节码就可以找到原因了,编译的字节码如下:
javap -c VarOptimize 警告: 文件 ./VarOptimize.class 不包含类 VarOptimize Compiled from "VarOptimize.java" public class com.example.optimize.VarOptimize { char[] myChars; public com.example.optimize.VarOptimize(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: ldc #7 // String Oracle Cloud Infrastructure Low data networking fees and automated migration Oracle Cloud Infrastructure platform is built for enterprises that are looking for higher performance computing with easy migration of their on-premises applications to the Cloud. 7: invokevirtual #9 // Method java/lang/String.toCharArray:()[C 10: putfield #15 // Field myChars:[C 13: return public static void main(java.lang.String[]); Code: 0: new #16 // class com/example/optimize/VarOptimize 3: dup 4: invokespecial #21 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #22 // Method globalVarTest:()V 12: aload_1 13: invokevirtual #25 // Method localityVarTest:()V 16: return public void globalVarTest(); Code: 0: iconst_0 1: istore_1 2: iconst_0 3: istore_2 4: iload_2 5: aload_0 6: getfield #15 // Field myChars:[C 9: arraylength 10: if_icmpge 33 13: aload_0 14: getfield #15 // Field myChars:[C 17: iload_2 18: caload 19: bipush 99 21: if_icmpne 27 24: iinc 1, 1 27: iinc 2, 1 30: goto 4 33: return public void localityVarTest(); Code: 0: aload_0 1: getfield #15 // Field myChars:[C 4: astore_1 5: iconst_0 6: istore_2 7: iconst_0 8: istore_3 9: iload_3 10: aload_1 11: arraylength 12: if_icmpge 32 15: aload_1 16: iload_3 17: caload 18: bipush 99 20: if_icmpne 26 23: iinc 2, 1 26: iinc 3, 1 29: goto 9 32: return }
其中关键的信息就在 getfield
关键字上,getfield
在此处的语义是从堆上获取变量,从上述的字节码可以看出 globalVarTest
方法在循环的内部每次都通过 getfield
关键字从堆上获取变量,而 localityVarTest
方法并没有使用 getfield
关键字,而是使用了出栈操作来进行业务处理,而从堆中获取变量比出栈操作要慢很多,因此使用全局变量会比局部变量慢很多。关于堆、栈的内容关注公众号「Java中文社群」我在后面的 JVM 优化的章节会单独讲解。
关于缓存
有人可能会说无所谓,反正使用全局变量会使用 CPU Cache,这样性能也和局部变量差不多,那我就随便用吧,反正也差不多。
但磊哥的建议是,能用局部变量的绝不使用全局变量,因为 CPU 缓存有以下 3 个问题:
- CPU Cache 采用的是 LRU 和 Random 的清除算法,不常使用的缓存和随机抽取一部分缓存会被删除掉,如果正好是你用的那个全局变量呢?
- CPU Cache 有缓存命中率的问题,也就是有一定的几率会访问不到缓存;
- 部分 CPU 只有两级缓存(L1 和 L2),因此可以使用的空间是有限的。
综上所述,我们不能把程序的执行性能完全托付给一个不那么稳定的系统硬件,所以能用局部变量坚决不要使用全局变量。
关键点:编写适合你的代码,在性能、可读性和实用性之间,找到属于你的平衡点!
总结
本文我们讲了局部变量的和全局变量的区别,如果使用全局变量会用 getfield
关键字从堆中获取变量,而局部变量则是通过出栈来获取变量的,因为出栈操作要比堆操作快很多,因此局部变量操作也会比全局变量快很多,所以建议你使用局部变量而不是全局变量。