分析字节码提升代码质量
Java字节码的内存和安全保护无论我们是否注意都是存在地那么我们为什么还费心查看字节码呢?在很多情况下知道编译器如何将你的代码转换为字节码可以帮助你写出更高效的代码而且在某些情况下可以防止不易发觉的错误考虑下面的例子
//返回 str+str 的串连
String concat(String str String str) {
return str + str;
}
//将 str 附加到 str
void concat(StringBuffer str String str) {
strappend(str);
}
猜猜每个方法需要多少个方法调用现在编译这些方法并且运行javap你会得到类似下面的输出
Method javalangString concat(javalangString javalangString)
new #
dup
invokespecial #
aload_
invokevirtual #
aload_
invokevirtual #
invokevirtual #
areturn
Method void concat(javalangStringBuffer javalangString)
aload_
aload_
invokevirtual #
pop
return
concat方法执行了个方法调用s: new invokespecial和三个invokevirtuals这比concat方法执行了更多的工作后者只执行了一个invokevirtual调用大多Java程序员已经得到过警告因为String是不可变的而使用StringBuffer进行字符串连接效率更高使用javap分析这个使得这点变得很生动如果你不能肯定两个语言构造在性能上是否相等你应该使用javap分析字节码然而对justintime (JIT)编译器要小心因为JIT编译器将字节码重新编译为本机代码而能执行一些javap不能揭示的附加优化除非你有你的虚拟机的源代码否则你应该补充你的字节码的基准性能分析
最后的一个范例展示了检查字节码如何帮助防止程序中的错误像下面那样创建两个类确保它们在独立的文件中
public class ChangeALot {
public static final boolean debug=false;
public static boolean log=false;
}
public class EternallyConstant {
public static void main(String [] args) {
Systemoutprintln(EternallyConstant beginning execution);
if (ChangeALotdebug)
Systemoutprintln(Debug mode is on);
if (ChangeALotlog)
Systemoutprintln(Logging mode is on);
}
}
如果你运行EternallyConstant你会得到信息
EternallyConstant beginning execution
现在试着编辑ChangeALot修改debug和log变量的值为true(两个都为true)只重新编译ChangeALot再次运行EternallyConstant你将看到下面的输出
EternallyConstant beginning execution
Logging mode is on
debug变量怎么了?即使你将debug设置为true信息Debug mode is on并没有出现答案在字节码中对 EternallyConstant运行javap你会看到
Method void main(javalangString[])
getstatic #
ldc #
invokevirtual #
getstatic #
ifeq
getstatic #
ldc #
invokevirtual #
return
惊奇吧!在log成员上有一个ifeq检查而代码根本没有检查debug成员因为debug成员被标记为final类型编译器知道debug成员在运行时永远不会改变因此它通过移除if声明进行优化这确实是一个非常有用的优化因为它允许你在程序中嵌入调试代码而在将它设置为false时不用付出运行时的代价不幸的是这个优化能够导致主要的编译时混乱如果你改变一个final成员你必须记住重新编译任何可能引用该成员的类这是因为这个reference可能已经经过优化了Java开发环境不能总是发现这个微妙的相关性一些能导致非常奇怪的错误因此古老的C++格言对于java环境仍然有效When in doubt rebuild all(有疑问重新编译所有的代码)
知道一些字节码的知识对于使用java编程的程序员都是有价值的javap工具使得查看字节码很容易有时候使用javap检查你的代码以期提高性能和捕获特殊的不易察觉的错误时是没有用的
[] []