关于javap的研究

来源:互联网 发布:网络小祖宗是什么意思 编辑:程序博客网 时间:2024/04/28 22:20

在学习Java的过程中,总会有一些疑难问题,需要涉及的虚拟机JVM的运行机制才能深入了解,比如,碰到了很多关于x++和++x的难题,以及最经典的String str = "abc" 共创建了几个对象,近几日听说了javap命令,网上百科之后了解到,有人称之为反汇编器,可以查看java编译器为我们生成的字节码。通过它,我们可以对照源代码和字节码,从而了解很多编译器内部的工作。

下面摘露一点百度百科上的参数使用说明。

当然,如果想分析这个文件,可以讲输出结果输出到一个文件里。可以这样写
  javap -c DocFooter > F://test.txt
  这样就会输出到F盘的test.txt文件中了。
选项:
  -help 不说了
  -l 输出行和变量的表
  -public 只输出public方法和域
  -protected 只输出public和protected类和成员
  -package 只输出包,public和protected类和成员,这是默认的
  -private 输出所有类和成员
  -s 输出内部类型签名
  -c 输出分解后的代码,例如,类中每一个方法内,包含java字节码的指令,
  -verbose 输出栈大小,方法参数的个数

==========================================================================

例1: 

Java代码  收藏代码
  1. public class JavapCTest{  
  2.     /** 
  3.      * @param args 
  4.      */  
  5.     public static void main(String[] args){  
  6.         int i = 2;  
  7.         int j = 3;  
  8.     }  
  9. }  

执行javac.JavapCTest.java,配置好自己的环境,再执行javap -c JavapCTest,得到如下代码: 
Code: 
Java代码  收藏代码
  1. [color=violet]  0:   iconst_2    //把2放到栈顶  
  2.   1:   istore_1    //把栈顶的值放到局部变量1中,即i中  
  3.   2:   iconst_3    //把3放到栈顶  
  4.   3:   istore_2    //把栈顶的值放到局部变量2中,即j中  
  5.   4:   return[/color]  

对于int i = 2;首先它会在栈中创建一个变量为i的引用,然后查找有没有字面值为2的地址,没找到,就开辟一个存放2这么字面值的地址,然后将i指向2的地址。 
例2: 
Java代码  收藏代码
  1. public class Difficult {  
  2.   
  3.  public static void main(String[] args)  
  4.   {  
  5.   int i=2;  
  6.   i=i++;  
  7.   int j=i++;  
  8.   System.out.println(i+":"+j);  
  9.   }  
  10. }  

输出结果: 
3:2 
Code: 
Java代码  收藏代码
  1. [color=violet]0: iconst_2 //将常数2压入栈中:2  
  2. 1: istore_1 //将栈顶的元素pop出,存入局部变量索引为1的位置:(栈中元素为空)  
  3. 2: iload_1 //将局部变量索引为1的int压入栈:2  
  4. 3: iinc 11 //将局部变量索引为1的值加1:2  
  5. 6: istore_1 //pop栈顶元素,将其存储到局部变量索引为1的位置:(栈中元素为空)  
  6. 7: iload_1 //将局部变量索引为1的int压入栈:2  
  7. 8: iinc 11 //将局部变量索引为1的值加1:2  
  8. 11: istore_2 //pop栈顶元素,将其存入局部变量索引为2的位置:(栈中元素为空)  
  9. 12return //返回:(栈中元素为空)[/color]  

例3: 
Java代码  收藏代码
  1. public class ByteCodeDemo {  
  2. public static void main(String[] args) {  
  3. System.out.println("Hello world");  
  4. }  
  5. }  

运行完javap命令,你会看到如下输出: 
Java代码  收藏代码
  1. public class ByteCodeDemo extends java.lang.Object {  
  2.  public ByteCodeDemo();  
  3.  public static void main(java.lang.String[]);  
  4. }  
  5. Method ByteCodeDemo()  
  6. 0 aload_0  
  7. 1 invokespecial #1 <Method java.lang.Object()>  
  8. 4 return  
  9. Method void main(java.lang.String[])  
  10. 0 getstatic #2 <Field java.io.PrintStream out>  
  11. 3 ldc #3 <String "Hello world">  
  12. 5 invokevirtual #4 <Method void println(java.lang.String)>  
  13. 8 return  

在这短小的列表中你可以学到很多字节码知识,从main方法第一个指令开始 
0 getstatic #2 ,开始的整数是方法中的指令的偏移值,因此第一个指令以0开始。紧随偏移量是指令的助记符(mnemonic)。在这个范例中,'getstatic' 指令将一个静态成员压入一个称为操作数堆栈的数据结构,后续的指令可以引用这个数据结构中的成员。getstatic 指令后是要压入的成员。在这个例子中,要压入的成员是"#2 " 。如果你直接检查字节码,你会看到成员信息没有直接嵌入指令而是像所有由java类使用的常量那样存储在一个共享池中。将成员信息存储在一个常量池中可以减小字节码指令的大小,因为指令只需要存储常量池中的一个索引而不是整个常量。在这个例子中,成员信息位于常量池中的#2处。常量池中的项目的顺序是和编译器相关的,因此在你的环境中看到的可能不是'#2' 。 

分析完第一个指令后很容易猜到其它指令的意思。'ldc' (load constant) 指令将常量"Hello, World."压入操作数栈。'invokevirtual'指令调用println方法,它从操作数栈弹出它的两个参数。不要忘记一个像println 这样的实例方法有两个参数:上面的字符串,加上隐含的'this'引用。 








原创粉丝点击