## 8.1 解释和编译
Java VM 使用了JIT(Just-in-time简称,程序即时编译) 编译器,在运行时刻进将字节码通过模板方式转为高效的机器码,也可以在运行时候通过分析,进一步编译成更加高效的机器码,因此现在Java程序,已经能跟C和C++具有一样的性能
对于操作系统来说,识别的是机器码指令,因此像C,C++高级语言都是将源程序编译成二进制码目标文件,然后链接成库文件或者可执行文件,这些二进制针对特定的平台,比如WINDOW,Linux。 这种方式成为AOT(Ahead-of-time,在执行执行前编译),对于PHP,则采用解释执行,可以运行在任何平台上,只要在平台上安装正确的解释器。解释器会翻译PHP代码成二进制代码执行,如果PHP被反复执行,则需要一遍又一遍翻译成机器码。解释执行的效率比编译后执行的效率低很多
> 注:PHP8也开始支持JIT
解释执行的优点是可以直接在任何平台上运行,不需要再编译,另外,如果代码更改,也可以直接解释执行而不需要再次编译成目标平台的机器码。
JVM的解释执行使用基于模板的解释器,比如,对于字节码
```
iload_1
invokestatic ...
```
第一个字节码`iload_1`映射成机器码
```
MOV -0x8(%r14), %eax # ILOAD_1
movzbl 0x1(%r13), %ebx # 下一个指令
inc %r13
mov $0xff40,%r10
jmpq *(%r10,%rbx,8)
```
虚拟机对于最常用的代码,会尝试编译成机器码放到"Code Cache 代码缓存“里,如下图所示

JIT也会做其他优化,比如,对于多态调用,采用的是虚方法表中查找,在JIT在执行多次后,如果收集到足够的信息,会取消多态掉调用的代码,改为直接调用。如果随后发现存在多态调用,则可能取消这部分优化,称之为逆优化(Deoptimization). 关于虚方法调用,可以参考8.6
对于小的方法,JIT也可以优化为内联调用,比如属性字段的getter和 setter方法
~~~java
public int getAge(){
return age;
}
~~~
当调用user.getAge()的时候,JIT会直接优化成user.age对应的机器码。关于内联优化,可以参考8.5。
通过虚拟机选项 -Xint 可以强制使用解释执行,如下JMH测试用于对比解释执行和编译机器码执行的性能
```java
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 10)
@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
@Threads(1)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class ClientTest {
int x=0,y=0;
@Benchmark
@Fork(value=1,jvmArgsAppend="-Xint")
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public int bytecode(){
return x+y;
}
@Benchmark
@Fork(value=1)
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public int machinecode(){
return x+y;
}
}
```
bytecode使用了`@Fork(value=1,jvmArgsAppend="-Xint")` 方法,解释执行。其性能远远低于machinecode方法
```
Benchmark Mode Score Units
c.i.c.j.ClientTest.bytecode avgt 202.944 ns/op
c.i.c.j.ClientTest.machinecode avgt 2.724 ns/op
```
- 内容介绍
- 第一章 java系统优化
- 第二章 字符串和数字
- 2.1 构造字符串
- 2.2 字符串拼接
- 2.3 字符串格式化
- 2.4 字符串查找
- 2.6 intern方法
- 2.7 StringUtils类
- 2.8 前缀树过滤
- 2.9 数字装箱
- 2.10 BigDecimal
- 第三章 并发和异步编程
- 3.1 不安全的代码
- 3.2 Java并发编程
- 3.2.1 volatile
- 3.2.2 synchronized
- 3.2.3 Lock
- 3.2.4 Condition
- 3.2.5 读写锁
- 3.2.6 semaphore
- 3.2.7 栅栏
- 3.3 Java并发工具
- 3.3.1 原子变量
- 3.3.2 Queue
- 3.3.3 Future
- 3.4 Java线程池
- 3.5 异步编程
- 3.5.1 创建异步任务
- 3.5.2 完成时回调
- 3.5.3 串行执行
- 3.5.4 并行执行
- 3.5.5 接收任务处理结果
- 第四章 代码性能优化
- 4.1 int 转 String
- 4.2 使用Native 方法
- 4.3 日期格式化
- 4.4 switch 优化
- 4.5 优先用局部变量
- 4.6 预处理
- 4.7 预分配
- 4.8 预编译
- 4.9 预先编码
- 4.10 谨慎使用Exception
- 4.11 批处理
- 4.12 展开循环
- 4.13 静态方法调用
- 4.14 高速Map存取
- 4.15 位运算
- 4.16 反射
- 4.17 压缩
- 4.18 可变数组
- 4.19 System.nanoTime()
- 4.20 ThreadLocalRandom
- 4.22 错误优化策略
- 4.22.1 final无法帮助内联
- 4.22.2 subString 内存泄露
- 4.22.3 循环优化
- 4.22.4 循环中捕捉异常
- 第五章 高性能工具
- 第六章 Java注释
- 6.1 JavaDoc
- 6.2 Tag
- 6.2.1 {@link}
- 6.2.2 @deprecated
- 6.2.3 {@literal}
- 6.2.4 {@code}
- 6.2.5 {@value}
- 6.2.6 @author
- 6.2.7 @param 和 @return
- 6.2.8 @throws
- 6.2.9 @see
- 6.2.10 自动拷贝
- 6.3 Package-Info
- 6.4 HTML生成
- 6.5 Markdown-doclet
- 第七章 可读性代码
- 7.1 精简注释
- 7.2 变量
- 7.2.1 变量命名
- 7.2.2 变量的位置
- 7.2.3 中间变量
- 7.3 方法
- 7.3 .1 方法签名
- 7.3.2 小方法
- 7.3.3 单一职责
- 7.3.3 小类
- 7.4 分支
- 7.4.1 if else
- 7.4.2 switch case
- 7.5 发现对象
- 7.3.1 不要用String
- 7.3.2 不要用数组,Map
- 7.6 checked异常
- 7.7 其他
- 7.7.1 避免自动格式化
- 7.7.2 关于Null
- 第八章 JIT优化
- 8.1 解释和编译
- 8.2 C1和C2
- 8.3 代码缓存
- 8.4 JITWatch
- 8.5 内联
- 8.6 虚方法调用
- 第九章 自测
- 9.1 ConcurrentHashMap陷阱
- 9.2 字符串搜索
- 9.3 IO输出
- 9.4 字符串拼接
- 9.5 方法的入参和出参
- 9.6 RPC调用定义的返回值
- 9.7 Integer使用
- 9.8 排序
- 9.9 判断特殊的ID
- 9.10 优化if结构
- 9.11 文件COPY
- 9.12 siwtch优化
- 9.13 Encoder
- 9.14一个JMH例子
- 9.15 注释
- 9.16 完善注释
- 9.17 方法抽取
- 9.18 遍历Map
- 9.19 日期格式化
- 9.20 日志框架设计的问题
- 9.21 持久化到数据库
- 9.22 某个RPC框架
- 9.23 循环调用
- 9.24 Lock使用
- 9.25 字符集
- 9.26 处理枚举值
- 9.27 任务执行
- 9.28 开关判断
- 9.29 JDBC操作
- 9.30 Controller代码
- 9.31 停止任务
- 9.32 log框架
- 第十章 ASM运行时增强
- 10.1 Java字节码
- 10.1.1 基础知识
- 10.1.2 class文件格式
- 10.2 Java方法的执行
- 10.2.1 方法在内存的表示
- 10.2.3 方法在class文件中的表示
- 10.2.3 指令的分类
- 10.2.4 操作数栈的变化分析
- 10.3 Bytecode Outline插件
- 10.4 ASM入门
- 10.4.1 生成类名及构造函数
- 10.4.2 生成main方法
- 10.4.3 调用生成的代码
- 10.5 ASM增强代码
- 10.5.1 使用反射实现
- 10.5.2 使用ASM生成辅助类
- 10.5.3 switch语句的分类
- 10.5.4 获取bean中的property
- 10.5.5 switch语句的实现
- 10.5.6 性能对比
- 第十一章 JSR269编译时增强
- 11.1 Java编译的过程
- 11.2 注解处理器入门
- 11.3 相关概念介绍
- 11.3.1 AbstractProcessor
- 11.3.2 Element与TypeMirror
- 11.4 注解处理器进阶
- 11.4.1 JsonWriter注解
- 11.4.2 处理器与生成辅助类
- 11.4.3 使用生成的Mapper类
- 11.4.4 注解处理器的使用
- 11.5 调试注解处理器
- 11.5.1 Eclipse中调试注解处理器
- 11.5.2 Idea中调试注解处理
- 附录A OQL分析JVM内存