在 JVM 中,String 是一个非常重要的类,这种微小的优化可能会提高一点启动速度。另一方面,BigInteger 对于 JVM 的启动并不重要。
所以,如果你看了这篇文章,自己也想在代码里面用这样的“棒”写法,三思。
醒醒吧,你才几个流量呀,值得你优化到这个程度?
而且,我就告诉你,前面字节码层面是有优化不假,我们都眼见为实了。
但是这个老哥提醒了我:
他提到了 JIT,是这样说的:这些微小的优化通常是不必要的,这只是减少了方法的字节码大小,一旦代码变得足够热而被 JIT 优化,它并不真正影响最终生成的汇编。
于是,我在 stackoverflow 上一顿乱翻,终于在万千线索中,找出了我觉得最有价值的一个。
这个问题,就和文章开头的读者问我的可以说一模一样了:
这个哥们说:在 jdk 源码中,更具体地说,是在集合框架中,有一个编码的小癖好,就是在表达式中读取变量之前,先将其赋值到一个局部变量中。这只是一个简单的小癖好吗,还是里面藏着一下我没有注意到的更重要的东西?
随后,还有人帮他补充了几句:
Doug Lea 是集合框架和并发包的主要作者之一,他编码的时候倾向于进行一些优化。但是这些优化这可能会违反直觉,让普通人感到困惑。
毕竟人家是在大气层。
接着他给出了一段代码,里面有三个方法,来验证了不同的写法生成的不同的字节码:
三个方法分别如下:
对应的字节码我就不贴了,直接说结论:
The testSeparate method uses 41 instructions
The testInlined method indeed is a tad smaller, with 39 instructions
Finally, the testRepeated method uses a whopping 63 instructions
同样的功能,但是最后一种直接使用成员变量的写法生成的字节码是最多的。
所以他给出了和我前面一样的结论:
这种写法确实可以节省几个字节的字节码,这可能就是使用这种方式的原因。
但是...
主要啊,他要开始 but 了:
但是,在不论是哪个方法,在被 JIT 优化之后,产生的机器代码将与原始字节码“无关”。
可以非常确定的是:三个版本的代码最终都会编译成相同的机器码(汇编)。
因此,他的建议是:不要使用这种风格,只需编写易于阅读和维护的“愚蠢”代码。你会知道什么时候轮到你使用这些“优化”。
可以看到他在“write dumb code”上附了一个超链接,我挺建议你去读一读的:
https://www.oracle.com/technical-resources/articles/javase/devinsight-1.html
在这里面,你可以看到《Java Concurrency in Practice》的作者 Brian Goetz:
他对于“dumb code”这个东西的解读:
说:通常,在 Java 应用程序中编写快速代码的方法是编写“dumb code”——简单、干净,并遵循最明显的面向对象原则的代码。
很明显,tab = table 这种写法,并不是 “dumb code”。
好了,说回这个问题。这个老哥接着做了进一步的测试,测试结果是这样的:
他对比了 testSeparate 和 TestInLine 方法经过 JIT 优化之后的汇编,这两个方法的汇编是相同的。
但是,你要搞清楚的是这个小哥在这里说的是 testSeparate 和 testInLine 方法,这两个方法都是采用了局部变量的方式:
只是 testSeparate 的可读性比 testInLine 高了很多。
而 testInLine 的写法,就是 HashMap 的写法。
所以,他才说:我们程序员可以只专注于编写可读性更强的代码,而不是搞这些“骚”操作。JIT 会帮我们做好这些东西。
从 testInLine 的方法命名上来看,也可以猜到,这就是个内联优化。
它提供了一种(非常有限,但有时很方便)“线程安全”的形式:它确保数组的长度(如 HashMap 的 getNode 方法中的 tab 数组)在方法执行时不会改变。
他为什么没有提到我们更关心的 testRepeated 方法呢?
他也在回答里面提到这一点:
他说:这都不用看,这三个方法最终生成的汇编肯定是一模一样的。
但是现在他说的是:
it can not result in the same machine code
它不能产生相同的汇编
最后,这个老哥还补充了这个写法除了字节码层面优化之外的另一个好处:
一旦在这里对 n 进行了赋值,在 getNode 这个方法中 n 是不会变的。如果直接使用数组的长度,假设其他方法也同时操作了 HashMap,在 getNode 方法中是有可能感知到这个变化的。
这个小知识点我相信大家都知道,很直观,不多说了。
但是,看到这里,我们好像还是没找到问题的答案。
那就接着往下挖吧。