JVM命令之javap

1 篇文章 0 订阅

一、序言

  就我个人而言,我想要搞清楚一个问题,就首先要明白以下几点:

      1、javap是什么

      2、用了javap有什么用为什么要用它(重点)

      3、学有所用,来个例子

   所以我想各位朋友们,如果一个问题对你来说没什么用处肯定不会深究它,只有足够的魅力才能够吸引你去学习它。下面我们开始按照以上三个方面来给大家解析javap

二、解析

   要想明白为什么使用反编译,首先要明白编译是什么,使用源语言程序变成目标程序的过程,编译的过程主要分为一下几个步骤:词法分析;语法分析;语义检查和中间代码生成;代码优化;目标代码生成。主要是进行词法分析和语法分析,又称为源程序分析,分析过程中发现有语法错误,给出提示信息。

   就以java为例。我们编写的是*.java文件,编译之后会便成为*.class文件,很明显的是*.java文件的代码我们是可以完全看得懂的,如果想要一个java文件能够运行在不同的机器上,是要经历很多的,首先第一步就是将java文件编译为class文件,class文件中的内容我们是看不懂的,都是一些二进制文件,然后通过我们的JVM解释(编译)成为不同的机器上面的机器码,其实class文件虽然是二进制文件,但是并不是我们系统可以识别的机器指令,而是JVM可以识别的指令,JVM有自己独立一套指令系统,所以class中的二进制指令就是JVM可以识别的。这就是为什么要进行编译,java为什么要变成class文件的原因。我们最常用的两个jdk命令就是javac、java,其中javac就是将java文件编译成为class文件。

  但是目前还是不了解为什么使用javap,不要急,马上就会讲解。我们想要了解java代码是怎么执行的,所以我们需要能动class文件,但是class文件又都是二进制,我们还真的看不懂,这时候jdk为我们提供了javap命令,它能够将class中反编译成为我们稍微能看懂的汇编语言,都是一些命令。可以帮助我们更好的理解java的运行机理,而不是像某些java反编译工具一样只是将class文件反编译成为java代码。

   给一下我在网上看到的反编译说法:计算机软件反向工程(Reverse engineering)也称为计算机软件还原工程,是指通过对他人软件的目标程序(可执行程序)进行“逆向分析、研究”工作,以推导出他人的软件产品所使用的思路、原理、结构、算法、处理过程、运行方法等设计要素,某些特定情况下可能推导出源代码。反编译作为自己开发软件时的参考,或者直接用于自己的软件产品中。说的很长,但是很专业,我是更喜欢自己的见解,就是将我们看不懂的低级语言转为我们认识的高级语言,和编译作用相反。

三、javap的使用

   

PS C:\Users\ThinkPad> javap
用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?        输出此用法消息
  -version                 版本信息
  -v  -verbose             输出附加信息
  -l                       输出行号和本地变量表
  -public                  仅显示公共类和成员
  -protected               显示受保护的/公共类和成员
  -package                 显示程序包/受保护的/公共类
                           和成员 (默认)
  -p  -private             显示所有类和成员
  -c                       对代码进行反汇编
  -s                       输出内部类型签名
  -sysinfo                 显示正在处理的类的
                           系统信息 (路径, 大小, 日期, MD5 散列)
  -constants               显示最终常量
  -classpath <path>        指定查找用户类文件的位置
  -cp <path>               指定查找用户类文件的位置
  -bootclasspath <path>    覆盖引导类文件的位置

   一般常用有-c -l -v

javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。
javap -l 会输出行号和本地变量表信息。
javap -c 会对当前class字节码进行反编译生成汇编代码。

查看汇编代码时,需要知道里面的jvm指令,可以参考官方文档:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html

上面都是英文,但是都是正统的,我是看不懂,所以就附加一个其他朋友写的链接,可以自行查看如果是分析class文件反编译文件:https://blog.csdn.net/zhangpan19910604/article/details/52254053

不说了,下面来个例子来详细解释一下:

四、例子

java代码


public class JavapTest {
	
	private final static String obj1 = "因为是final需要初始化";
	private final String obj2 = "同上";
	private static String obj;
	private String obj4;
	private Integer obj5;
	
	public void method1() {
		System.out.println("this is my first method....");
	}
	
	public void method2() {
		System.out.println("this is my second method....");
		return ;
	}
	
	public Integer method3() {
		System.out.println("this is my third method....");
		return 123;
	}
	
	public void method() {
		System.out.println("this is my forth method....");
		method1();
		method2();
		int result = method3();
		System.out.println("result = " + result);
	}
	
	public static void main(String[] args) {
		JavapTest test = new JavapTest();
		test.method();
	}
}

反编译命令

PS E:\eclipse\JVMDemo\bin> javap -c .\JavapTest.class > javap-c.txt

生成的文件:

解析反编译之后的内容

 

Compiled from "JavapTest.java"
public class JavapTest {
  public JavapTest();
    Code:
       0: aload_0														aload_0 表示对this的操作,在static 方法中,aload_0表示对方法的第一参数的操作。
       1: invokespecial #20                 // Method java/lang/Object."<init>":()V			调用超类构造方法、实例初始化方法、私有方法
       4: aload_0														aload_0 表示对this的操作,在static 方法中,aload_0表示对方法的第一参数的操作。
       5: ldc           #11                 // String 同上								将int、float或String型常量值从常量池中推送至栈顶
       7: putfield      #22                 // Field obj2:Ljava/lang/String;					为指定的类的实例域赋值
      10: return														当前方法返回void

  public void method1();
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;	获取指定类的静态域,并将其值压入栈顶
       3: ldc           #35                 // String this is my first method....				将int、float或String型常量值从常量池中推送至栈顶
       5: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V		调用实例方法
       8: return										当前方法返回void

  public void method2();
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #44                 // String this is my second method....
       5: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return

  public java.lang.Integer method3();
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #48                 // String this is my third method....
       5: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: bipush        123								将一个byte型常量值推送至栈顶
      10: invokestatic  #50                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;		调用静态方法
      13: areturn

  public void method();
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #57                 // String this is my forth method....
       5: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: aload_0
       9: invokevirtual #59                 // Method method1:()V
      12: aload_0
      13: invokevirtual #61                 // Method method2:()V
      16: aload_0
      17: invokevirtual #63                 // Method method3:()Ljava/lang/Integer;
      20: invokevirtual #65                 // Method java/lang/Integer.intValue:()I
      23: istore_1						将栈顶int型数值存入第二个局部变量
      24: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
      27: new           #69                 // class java/lang/StringBuilder
      30: dup
      31: ldc           #71                 // String result =
      33: invokespecial #73                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      36: iload_1							第二个int型局部变量进栈,从0开始计数
      37: invokevirtual #75                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      40: invokevirtual #79                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      43: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      46: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class JavapTest
       3: dup
       4: invokespecial #87                 // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #88                 // Method method:()V
      12: return
}

 

   五、JVM相关内容比较多,要想将JVM搞清楚确实不容易,这篇文章主要是讲解java的反编译,关于编译的过程下篇文章讲解。

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值