都是通过即时编译器优化生成的IR图来精简最后的机器码(在生成机器码前就好像人浏览了一遍代码,通过前后关联情况手动消除了一些重复或无效的代码),去掉无效代码,减少在code cache中存储的机器码的大小,节省内存,提高程序运行速度。
主要包括:缓存读取、去除重复操作、分支优化、不可达分支消除
一、对象字段读取优化
优化一:缓存读取
static int bar(Foo o, int x) {
int y = o.a + x; // 将o.a存入缓存
return o.a + y; // 直接从缓存读取o.a(IR图中直接从缓存取值)
}
优化二:去重去不可达分支,可达分支优化
static int bar(Foo o, int x) {
o.a = 1;
if (o.a >= 0) // 缓存中o.a为1,该行判断可以默认为true(IR图中自动消除该行)
return x;
else // 冗余无效代码,(IR图中自动消除该else分支)
return -x;
}
// 优化后程序
static int bar(Foo o, int x) {
o.a = 1;
return x;
}
二、对象字段存储优化
同一个字段先后被存储两次,两次操作中间没有对该字段的读取操作,没有被方法调用或者没有被间接存储到其他字段,JVM会消除第一处的冗余赋值指令(Volatile同样可以阻止该优化,强制属性值实时刷入内存)
class Foo {
int a = 0; // 冗余重复代码,被优化(IR图中被去除)
void bar() {
a = 1; // 冗余重复代码,被优化(IR图中被去除)
a = 2; // 最终执行的赋值语句
}
}
// 优化后程序
class Foo {
void bar() {
a = 2; // 最终执行的赋值语句
}
}
三、局部变量死存储优化
与对象的字段值一样包含去除重复或者无效代码,优化分支选择。
// 重复代码消除
int bar(int x, int y) {
int t = x*y; // 重复冗余代码,IR图中被去除
t = x+y;
return t;
}
// 分支代码优化
int bar(boolean f, int x, int y) {
int t = x*y; // 编译后此处为int t ,具体赋值操作留到if程序块中
if (f)
t = x+y;
return t; // 如果走这条路才会对t进行第一行的赋值操作(int t = x*y) 并返回
}
// 不可达分支
int bar(int x) {
if (false) // 不可达分支,IR图中不会被编译
return x;
else
return -x;
}
ps :可能出现异常时,无法进行字段优化
// 除零异常
int bar(int x, int y) {
int t = x/y; // 由于可能出现y=0的算术操作异常,所以该行不能被优化去除
t = x+y;
return t;
}
参考文章:
https://time.geekbang.org/column/article/39683