- 作者:xiaoxiao
- 发表时间:2020-12-23 10:59
- 来源:未知
字节码防止内存错误及提高代码质量 作者:cherami 翻译 加入时间:2003-12-16 13:44:29 开始使用JAVAP
大多Java程序员知道他们的程序通常不会被编译为本机代码而是被编译为由java虚拟机(JVM)执行的字节码格式。然而,很少有java程序员曾经看过字节码因为他们的工具不鼓励他们去看。大多Java 调试工具不允许单步执行字节码,它们要么显示源代码行,要么什么也不显示。幸运的是JDK提供了javap,一个命令行工具,它使得查看字节码很容易。让我们看一个范例:
public class ByteCodeDemo {public static void main(String[] args) {System.out.println("Hello world");}}
在编译这个类后,你可以用十六进制编辑器打开.class文件然后参照虚拟机规范翻译字节码。幸运的是有更简单的方法。JDK包含一个命令行的反汇编器:javap,它可以转换字节码为一种可读的助记符形式,可以像下面这样通过传递'-c'参数给javap得到字节码列表:
javap -c ByteCodeDemo
你应该会看到输出类似这样:
public class ByteCodeDemo extends java.lang.Object {public ByteCodeDemo();public static void main(java.lang.String[]);}Method ByteCodeDemo()0 aload_01 invokespecial #1 4 returnMethod void main(java.lang.String[])0 getstatic #2 3 ldc #3 5 invokevirtual #4 8 return
仅仅从这个短小的列表你可以学到很多字节码的知识。从main方法的第一个指令开始:
0 getstatic #2
开始的整数是方法中的指令的偏移值,因此第一个指令以0开始。紧随偏移量是指令的助记符(mnemonic)。在这个范例中,'getstatic' 指令将一个静态成员压入一个称为操作数堆栈的数据结构,后续的指令可以引用这个数据结构中的成员。getstatic 指令后是要压入的成员。在这个例子中,要压入的成员是"#2 " 。如果你直接检查字节码,你会看到成员信息没有直接嵌入指令而是像所有由java类使用的常量那样存储在一个共享池中。将成员信息存储在一个常量池中可以减小字节码指令的大小,因为指令只需要存储常量池中的一个索引而不是整个常量。在这个例子中,成员信息位于常量池中的#2处。常量池中的项目的顺序是和编译器相关的,因此在你的环境中看到的可能不是'#2' 。
分析完第一个指令后很容易猜到其它指令的意思。'ldc' (load constant) 指令将常量"Hello, World."压入操作数栈。'invokevirtual'指令调用println方法,它从操作数栈弹出它的两个参数。不要忘记一个像println这样的实例方法有两个参数:上面的字符串,加上隐含的'this'引用。
字节码如何预防内存错误