![5a7cc2fbb1cfea581f657b9c36e57a48.png](https://i-blog.csdnimg.cn/blog_migrate/23ef94d4a9c16bf551a431dc0a00d39b.jpeg)
今天这篇文章,我们通过一个故事来深入聊聊 Java 编译背后的秘密。
![78adeca7ff3f0baea254abb8ccbbce06.png](https://i-blog.csdnimg.cn/blog_migrate/a3a10cecba9669aba3d488beb3eb95f2.jpeg)
![8f3d3979d5b217f50ea8f80893b2694a.png](https://i-blog.csdnimg.cn/blog_migrate/255f075e6cd4649d055cdb0f37c95c3a.jpeg)
![8540e3593437e85c4792010ad79e73de.png](https://i-blog.csdnimg.cn/blog_migrate/84be2faa6be1b3fed7239e2821a8a2f2.jpeg)
![ebf1f5aaebf5d3d6d47823bdbc8d8661.png](https://i-blog.csdnimg.cn/blog_migrate/d78ecc56db8c167909735047d85a4f9e.jpeg)
![748d7db8d9a6e3dbfa2f5e3cb0ecb591.png](https://i-blog.csdnimg.cn/blog_migrate/ce52f21a62ca8fb36520228678c7fdc0.jpeg)
东哥说这段代码来自于 Stackoverflow(关于这个网站,东哥似乎分享了无数次《这三个网站的使用技巧,你值得收藏》,真正去逛这个网站的同学还是很少),如下。
public static void main(String[] args) {
long startTime = System.nanoTime();
int n = 0;
for (int i = 0; i < 1000000000; i++) {
n += 2 * i * i;
}
System.out.println((double) (System.nanoTime() - startTime) / 1000000000 + " s");
System.out.println("n = " + n);
}
代码很简单吧,它的执行时间大概在 0.60s ~ 0.65s。但是如果你把 2*i*i 替换成 2*(i*i),执行时间大概在 0.50s ~ 0.55s。
对这段程序的两个版本分别执行 15 次,得到的结果如下。
![8ee8e06392793db12ca32ab638cc1a73.png](https://i-blog.csdnimg.cn/blog_migrate/c8cff4246bedaa3a127e537440298e24.jpeg)
我们可以看出 2*(i*i) 比 2*i*i 快 。
![81c0a62da677eb8b110175181ce32381.png](https://i-blog.csdnimg.cn/blog_migrate/d238c038855a6b6f7d7b5bc673f30fe0.jpeg)
我们来分别查看它的字节码,这里东哥给我推荐了一款好用的 IDEA 插件,叫做 jclasslib bytecode viewer(你还不会用的话,可以去查看一下东哥一系列的 IDEA 文章《用 IDEA 跟踪 Java 源码的技巧 | 内部资料》,真心不错)。
2*i*i 的字节码如下。
![05e46768f2a8f0fd0b5c710894875f69.png](https://i-blog.csdnimg.cn/blog_migrate/55b7c9278593d143bf1ab5b4446581c9.jpeg)
2*(i*i) 的字节码如下。
![630eec9facb570cb8449e3ff3261bd90.png](https://i-blog.csdnimg.cn/blog_migrate/d7e30865c8cd4399c88bbfaa218d7e0c.jpeg)
我们可以发现除了字节码顺序不同外,没有其它异常,下一步该怎么办呢?
![2978ff91e428bd73cf37de935c771692.png](https://i-blog.csdnimg.cn/blog_migrate/cfee2b4dae901a9442effd381f9ac0b4.jpeg)
这里东哥给我推荐一款开源的工具,叫做 jitwatch,它可以查看查看分析HotSpot JIT compiler 生成的汇编代码,关于它的安装方法可以通过谷歌查阅。
查看是否安装成功?可以用下面的命令。
![89d59115d500a74f055ec3a6f77593a0.png](https://i-blog.csdnimg.cn/blog_migrate/9ff99e716438b33326c7cbd8ab5bef9d.jpeg)
如何查看汇编代码?
运行时,添加参数 -XX:+PrintAssembly。
好了,那我们来分别看看它们的汇编代码,由于汇编代码很多,我这里就不贴出来了,各位同学可以去运行分析一下。
我这里就说一下结论,通过对比分析,我们会发现,2*i*i 进行了大量的堆栈操作,因此,需要保存大量的中间结果;而 2*(i*i) 只有少量的堆栈操作。
显而易见,2*(i*i) 比 2*i*i 快是由于 JIT 优化的结果。
![19de111e00c05aaed2b1c26f28323d00.png](https://i-blog.csdnimg.cn/blog_migrate/54d7728eb52978f69d80ab62d549072f.jpeg)
说明:
本文资料来自于Stackoverflow、Github。东哥的第一篇漫画文章,如果喜欢的话,转发支持一下,给东哥继续创作下去的动力。