JVM性能调优实战之jvm优化和tomcat优化( 五 )


内联
许多优化手段都试图消除机器级跳转指令(例如 , x86架构的JMP指令) 。跳转指令会修改指令指针寄存器 , 因此而改变了执行流程 。相比于其他汇编指令 , 跳转指令是一个代价高昂的指令 , 这也是为什么大多数优化手段会试图减少甚至是消除跳转指令 。内联是一种家喻户晓而且好评如潮的优化手段 , 这是因为跳转指令代价高昂 , 而内联技术可以将经常调用的、具有不容入口地址的小方法整合到调用方法中 。Listing 3到Listing 5中的Java代码展示了使用内联的用法 。
Listing 3. Caller method
int whenToEvaluateZing(int y) { return daysLeft(y) + daysLeft(0) + daysLeft(y+1);}
Listing 4. Called method
int daysLeft(int x){ if (x == 0) return 0; else return x - 1;}
Listing 5. Inlined method
int whenToEvaluateZing(int y){ int temp = 0;if(y == 0) temp += 0; else temp += y - 1; if(0 == 0) temp += 0; else temp += 0 - 1; if(y+1 == 0) temp += 0; else temp += (y + 1) - 1;return temp; }在Listing 3到Listing 5的代码中 , 展示了将调用3次小方法进行内联的示例 , 这里我们认为使用内联比跳转有更多的优势 。
如果被内联的方法本身就很少被调用的话 , 那么使用内联也没什么意义 , 但是对频繁调用的“热点”方法进行内联在性能上会有很大的提升 。此外 , 经过内联处理后 , 就可以对内联后的代码进行进一步的优化 , 正如Listing 6中所展示的那样 。
Listing 6. After inlining, more optimizations can be applied
int whenToEvaluateZing(int y){ if(y == 0) return y; else if (y == -1) return y - 1; else return y + y - 1;}循环优化
当涉及到需要减少执行循环时的性能损耗时 , 循环优化起着举足轻重的作用 。执行循环时的性能损耗包括代价高昂的跳转操作 , 大量的条件检查 , 和未经优化的指令流水线(即引起CPU空操作或额外周期的指令序列)等 。循环优化可以分为很多种 , 在各种优化手段中占有重要比重 。其中值得注意的包括以下几种:

  • 合并循环:当两个相邻循环的迭代次数相同时 , 编译器会尝试将两个循环体进行合并 。当两个循环体中没有相互引用的情况 , 即各自独立时 , 可以同时执行(并行执行) 。
  • 反转循环:基本上将就是用do-while循环体换掉常规的while循环 , 这个do-while循环嵌套在if语句块中 。这个替换操作可以节省两次跳转操作 , 但是 , 会增加一个条件检查的操作 , 因此增加的代码量 。这种优化方式完美的展示了以少量增加代码量为代价换取较大性能的提升 —— 编译器需要在运行时需要权衡这种得与失 , 并制定编译策略 。
  • 分块循环:重新组织循环体 , 以便迭代数据块时 , 便于缓存的应用 。
  • 展开循环:减少判断循环条件和跳转的次数 。你可以将之理解为将一些迭代的循环体“内联”到一起 , 而无需跨越循环条件 。展开循环是有风险的 , 它有可能会降低应用程序的运行性能 , 因为它会影响流水线的运行 , 导致产生了冗余指令 。再强调一遍 , 展开循环是编译器在运行时根据各种信息来决定是否使用的优化手段 , 如果有足够的收益的话 , 那么即使有些性能损耗也是值得的 。
  • 至此 , 已经简要介绍了编译器对字节码层级(以及更底层)进行优化 , 以提升应用程序在目标平台的执行性能的几种方式 。这里介绍的几种优化手段是比较常用的几种 , 只是众多优化技术中的几种 。在介绍优化方法时配以简单示例和相关解释 , 希望可以洗发你进行深度探索的兴趣 。更多相关内容请参见相关资源 。