Java 优化策略

master
Omooo 6 years ago
parent c6c7298abd
commit f6a1cd73a9
  1. 7
      README.md
  2. 37
      blogs/JVM/Java 优化策略.md

@ -129,7 +129,10 @@ Class 文件格式
- [JVM 垃圾收集器与内存分配策略](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/JVM%20%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8%E4%B8%8E%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E7%AD%96%E7%95%A5.md)
- [类加载机制与双亲委派模型](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6%E4%B8%8E%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B.md)
[方法内联](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/%E6%96%B9%E6%B3%95%E5%86%85%E8%81%94.md)
[JVM 优化 Java 代码时都做了什么?](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/Java%20%E4%BC%98%E5%8C%96%E7%AD%96%E7%95%A5.md)
- [方法内联](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/%E6%96%B9%E6%B3%95%E5%86%85%E8%81%94.md)
- [循环优化](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/%E5%BE%AA%E7%8E%AF%E4%BC%98%E5%8C%96.md)
[一篇文章搞懂 synchronized、CAS、AQS](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/锁.md)
@ -137,8 +140,6 @@ Class 文件格式
[JVM 是如何执行方法调用的?](https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/JVM%20%E6%98%AF%E5%A6%82%E4%BD%95%E6%89%A7%E8%A1%8C%E6%96%B9%E6%B3%95%E8%B0%83%E7%94%A8%E7%9A%84%EF%BC%9F.md)
[循环优化](https://github.com/Omooo/Android-Notes/blob/master/blogs/Java/%E5%BE%AA%E7%8E%AF%E4%BC%98%E5%8C%96.md)
#### 设计模式
[单例模式](https://github.com/Omooo/Android-Notes/blob/master/blogs/DesignMode/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F.md)

@ -0,0 +1,37 @@
---
JVM 优化 Java 代码时都做了什么?
---
#### 典型回答
来自 [郑雨迪老师](https://time.geekbang.org/column/intro/108?utm_source=app&utm_medium=article&utm_campaign=108-presell&utm_content=java) 的回答:
JVM 在对代码执行的优化可分为运行时(Runtime)和即时编译器(JIT)优化。运行时优化主要是解释执行和动态编译通用的一些机制,比如说锁机制(如偏斜锁)、内存分配机制(如 TLAB)等。除此之外,还有一些专门用于优化解释执行效率的,比如说模版解释器、内联缓存(inline cache,用于优化虚方法调用的动态绑定)。
JVM 的即时编译器优化是指将热点代码以方法为单位转换成机器码,直接运行在底层硬件之上。它采用了多种优化方式,包括静态编译器可以使用的如方法内联、逃逸分析,也包括基于程序运行 profile 的投机性优化(speculative/optimistic optimization)。这个怎么理解呢?比如我有一条 instanceof 指令,在编译之前的执行过程中,测试对象的类一直是同一个,那么即时编译器可以假设编译之后的执行过程中还会是这一个类,并且根据这个类直接返回 instanceof 的结果。如果出现了其他类,那么就抛弃这段编译后的机器码,并且切换回解释执行。
当然,JVM 的优化方式仅仅作用在运行应用代码的时候。如果应用代码本书阻塞了,比如说并发时等待另一线程的结果,这就不在 JVM 的优化范畴了。
#### 知识扩展
通常所说的编译器,是指 javac 等编译器或者相关 API 等将源码转换成字节码的过程,这个阶段也会进行少量类似常量折叠之类的优化,只要利用反编译工具,就可以直接查看细节。
javac 优化与 JVM 内部优化也存在关联,毕竟它负责了字节码的生成。例如,Java 9 中的字符串拼接,会被 javac 替换成对 StringConcatFactory 的调用,进而为 JVM 进行字符串拼接优化提供了统一的入口。
然后就是重点要讲的 JVM 运行时优化,在通常情况下,编译器和解释器是共同起作用的。
JVM 会根据统计信息,动态决定什么方法被编译,什么方法解释执行,即使是已经编译过的代码,也可能在不同的运行阶段不再是热点,JVM 有必要将这种代码从 Code Cache 中移除出去,毕竟其大小是有限的。
解释器和编译器也会进行一些通用优化,例如:
- 锁优化
- Intrinsic 机制
或者叫做内建方法,就是针对特别重要的基础方法,JDK 团队直接提供定制的实现,利用汇编或者编译器的中间表达方式编写,然后 JVM 会直接在运行时进行替换。
这么做的理由有很多,例如,不同体系结构的 CPU 在指令等层面存在着差异,定制才能充分发挥出硬件的能力。我们日常使用的典型的字符串操作、数组拷贝等基础方法,Hotspot 都提供了内建实现。
而即时编译器,则是更多优化工作的承担者。JIT 对 Java 编译的基本单元是整个方法,通过对方法调用的计数统计,编译为本地代码。另外一个优化场景,则是针对所谓的热点循环代码,利用通常说的栈上替换技术(OSR),如果方法本身的调用频度还不够编译标准,但是内部有大的循环之类,则还是会有进一步优化的价值。
从理论上来看,JIT 可以看作就是基于两个计数器实现,方法计数器和回边计数器提供给 JVM 统计数据,以定位到热点代码。实际中的 JIT 机制要复杂的多,比如有逃逸分析、循环展开、方法内联等,包括前面提到的 Intrinsic 等通用机制同样会在 JIT 阶段发生。
Loading…
Cancel
Save