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


Figure 1中展示了纯解释运行、客户端模式运行、服务器端模式运行和层次编译模式运行下性能之间的区别 。X轴表示运行时间(单位时间)Y轴表示性能(每单位时间内的操作数) 。

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

文章插图
Figure 1. Performance differences between compilers (click to enlarge)
编译性能对比
相比于纯解释运行的的代码 , 以客户端模式编译运行的代码在性能(指单位时间执行的操作)上可以达到约5到10倍 , 因此而提升了应用程序的运行性能 。其间的区别主要在于编译器的效率、编译器所作的优化 , 以及应用程序在设计实现时针对目标平台做了何种程度的优化 。实际上 , 最后一条不在Java程序员的考虑之列 。
相比于客户端编译器 , 使用服务器端编译器通常会有30%到50%的性能提升 。在大多数情况下 , 这种程度的性能提升足以弥补使用服务器端编译所带来的额外资源消耗 。
层次编译综合了服务器端编译器和客户端编译器的优点 , 使用客户端编译模式实现快速启动和快速优化 , 使用服务器端编译模式在后续的执行周期中完成高级优化的编译任务 。
常用编译优化手段
到目前为止 , 已经介绍了优化代码的价值 , 以及常用JVM编译器是如何以及何时编译代码的 。接下来 , 将用一些实际的例子做个总结 。JVM所作的性能优化通常在字节码这一层级(或者是更底层的语言表示) , 但这里我将使用Java编程语言对优化措施进行介绍 。在这一节中 , 我无法涵盖JVM中所作的所有性能优化 , 相反 , 我希望可以激发你的兴趣 , 使你主动挖掘并学习编译器技术中所包含了数百种高级优化技术(参见相关资源) 。
死代码剔除
死代码剔除指的是 , 将用于无法被调用的代码 , 即“死代码” , 从源代码中剔除 。如果编译器在运行时发现某些指令是不必要的 , 它会简单的将其从可执行指令集中剔除 。例如 , 在Listing 1中 , 变量被赋予了确定值 , 却从未被使用 , 因此可以在执行时将其完全忽略掉 。在字节码这一层级 , 也就不会有将数值载入到寄存器的操作 。没有载入操作意味着可以更少的CPU时间 , 更好的运行性能 , 尤其是当这段代码是“热点”代码的时候 。
Listing 1中展示了示例代码 , 其中被赋予了固定值的代码从未被使用 , 属于无用不必要的操作 。
Listing 1. Dead code
int timeToScaleMyApp(boolean endlessOfResources) { int reArchitect = 24; int patchByClustering = 15; int useZing = 2;if(endlessOfResources) return reArchitect + useZing; else return useZing;}在字节码这一层级 , 如果变量被载入但从未使用 , 编译器会检测到并剔除这个死代码 , 如Listing 2所示 。剔除死代码可以节省CPU时间 , 从而提升应用程序的运行速度 。
Listing 2. The same code following optimization
int timeToScaleMyApp(boolean endlessOfResources) { int reArchitect = 24; //unnecessary operation removed here... int useZing = 2;if(endlessOfResources) return reArchitect + useZing; else return useZing;}
冗余剔除是一种类似的优化手段 , 通过剔除掉重复的指令来提升应用程序性能 。