通过反编译生成的字节码文件,我们可以深入的了解Java代码的工作机制。但是,自己分析类文件结构太麻烦了!除了使用第三方的jclasslib工具之外,oracle官方也提供了工具:javap。
javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区、局部变量表、异常表和代码行偏移量映射表、常量池等信息。
通过局部变量表,我们可以产看局部变量的作用域范围、所在槽位等信息,甚至可以看到槽位复用等信息。
javac -g *** 作解析字节码文件得到的信息中,有些信息(如局部变量表、指令和代码行偏移量映射表、常量池中方法的参数名称等等)需要在使用javac编译成class文件时,指定参数才能输出。
比如,直接javac xx.java,就不会生成对应的局部变量表等信息,如果你使用javac -g xx.java就可以生成所有相关信息了。如果你使用的eclipse或者IDEA,则默认情况下,eclipse、IDEA在编译时会帮你生成局部变量表、指令和代码行偏移量映射表等信息。
javap的用法javap的用法格式:
javap
其中,classes就是要反编译的class文件。
在命令行中直接输入javap或javap -help可以看到javap的options选项:
Usage: javap使用举例where possible options include: -help --help -? Print this usage message -version Version information -v -verbose Print additional information -l Print line number and local variable tables -public Show only public classes and members -protected Show protected/public classes and members -package Show package/protected/public classes and members (default) -p -private Show all classes and members -c Disassemble the code -s Print internal type signatures -sysinfo Show system info (path, size, date, MD5 hash) of class being processed -constants Show final constants -classpath Specify where to find user class files -cp Specify where to find user class files -bootclasspath Override location of bootstrap class files
1.代码
package com.example.jvm; public class SeniorDemo { private int num = 1; public final String info = "hello world"; boolean[] counts; public SeniorDemo() { } public SeniorDemo(int count) { this.counts = new boolean[count]; } public String getInfo() { return info; } public void addNum(int n) { num += n; System.out.println(num); } }
- javap -v SeniorDemo.class
Classfile /home/mall/work/gitrepository/jvm/out/production/jvm/SeniorDemo.class Last modified Nov 11, 2021; size 852 bytes MD5 checksum 0d32355daa6291e418aba33a4302a55d Compiled from "SeniorDemo.java" public class SeniorDemo // 类名称 minor version: 0 major version: 52 // 主版本号和副版本号组合,得出编译该文件的jdk版本 flags: ACC_PUBLIC, ACC_SUPER // 方法标识 #####################################常量池开始######################################## Constant pool: #1 = Methodref #9.#32 // java/lang/Object."总结":()V #2 = Fieldref #6.#33 // SeniorDemo.num:I #3 = String #34 // hello world #4 = Fieldref #6.#35 // SeniorDemo.info:Ljava/lang/String; #5 = Fieldref #6.#36 // SeniorDemo.counts:[Z #6 = Class #37 // SeniorDemo #7 = Fieldref #38.#39 // java/lang/System.out:Ljava/io/PrintStream; #8 = Methodref #40.#41 // java/io/PrintStream.println:(I)V #9 = Class #42 // java/lang/Object #10 = Utf8 num #11 = Utf8 I #12 = Utf8 info #13 = Utf8 Ljava/lang/String; #14 = Utf8 ConstantValue #15 = Utf8 counts #16 = Utf8 [Z #17 = Utf8 #18 = Utf8 ()V #19 = Utf8 Code #20 = Utf8 LineNumberTable #21 = Utf8 LocalVariableTable #22 = Utf8 this #23 = Utf8 LSeniorDemo; #24 = Utf8 (I)V #25 = Utf8 count #26 = Utf8 getInfo #27 = Utf8 ()Ljava/lang/String; #28 = Utf8 addNum #29 = Utf8 n #30 = Utf8 SourceFile #31 = Utf8 SeniorDemo.java #32 = NameAndType #17:#18 // " ":()V #33 = NameAndType #10:#11 // num:I #34 = Utf8 hello world #35 = NameAndType #12:#13 // info:Ljava/lang/String; #36 = NameAndType #15:#16 // counts:[Z #37 = Utf8 SeniorDemo #38 = Class #43 // java/lang/System #39 = NameAndType #44:#45 // out:Ljava/io/PrintStream; #40 = Class #46 // java/io/PrintStream #41 = NameAndType #47:#24 // println:(I)V #42 = Utf8 java/lang/Object #43 = Utf8 java/lang/System #44 = Utf8 out #45 = Utf8 Ljava/io/PrintStream; #46 = Utf8 java/io/PrintStream #47 = Utf8 println #####################################常量池结束######################################## #####################################字段表开始######################################## { public final java.lang.String info; descriptor: Ljava/lang/String; flags: ACC_PUBLIC, ACC_FINAL ConstantValue: String hello world boolean[] counts; descriptor: [Z flags: #####################################字段表结束######################################## #####################################方法表开始######################################## public SeniorDemo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 // *** 作数栈长度,局部变量表长度,参数个数 0: aload_0 1: invokespecial #1 // Method java/lang/Object." ":()V 4: aload_0 5: iconst_1 6: putfield #2 // Field num:I 9: aload_0 10: ldc #3 // String hello world 12: putfield #4 // Field info:Ljava/lang/String; 15: return LineNumberTable: // 行号表,代码行号与上面的code中的行号的对应 line 7: 0 line 3: 4 line 4: 9 line 9: 15 LocalVariableTable: // 局部变量表,start标识局部变量的开始作用域,与code中的相对于 Start Length Slot Name Signature 0 16 0 this LSeniorDemo; // 表明了作用域,变量名称,变量类型,也能看出槽位有没有被覆用的情况 public SeniorDemo(int); descriptor: (I)V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: invokespecial #1 // Method java/lang/Object." ":()V 4: aload_0 5: iconst_1 6: putfield #2 // Field num:I 9: aload_0 10: ldc #3 // String hello world 12: putfield #4 // Field info:Ljava/lang/String; 15: aload_0 16: iload_1 17: newarray boolean 19: putfield #5 // Field counts:[Z 22: return LineNumberTable: line 11: 0 line 3: 4 line 4: 9 line 12: 15 line 13: 22 LocalVariableTable: Start Length Slot Name Signature 0 23 0 this LSeniorDemo; 0 23 1 count I public java.lang.String getInfo(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: ldc #3 // String hello world 2: areturn LineNumberTable: line 16: 0 LocalVariableTable: Start Length Slot Name Signature 0 3 0 this LSeniorDemo; public void addNum(int); descriptor: (I)V flags: ACC_PUBLIC Code: stack=3, locals=2, args_size=2 0: aload_0 1: dup 2: getfield #2 // Field num:I 5: iload_1 6: iadd 7: putfield #2 // Field num:I 10: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_0 14: getfield #2 // Field num:I 17: invokevirtual #8 // Method java/io/PrintStream.println:(I)V 20: return LineNumberTable: line 20: 0 line 21: 10 line 22: 20 LocalVariableTable: Start Length Slot Name Signature 0 21 0 this LSeniorDemo; 0 21 1 n I #####################################方法表结束######################################## } #####################################属性开始######################################## SourceFile: "SeniorDemo.java" // 表明类源文件的名称 #####################################属性结束########################################
- 通过javap命令可以查看一共java类反汇编得到的Class文件版本号、常量池、访问标识、变量表、指令代码行号表等信息。不显式类索引、父类索引、接口类索引集合、
()、 等结构。 - 通过对前面例子代码反汇编文件的简单分析,可以发现,一个方法的执行通常会涉及下面几块内存的 *** 作:
- java栈中,局部变量表、 *** 作数栈
- java堆中,通过对象的地址引用去 *** 作
- 常量池
- 其他如帧数据区、方法区的剩余部分等情况。
- 平时,我们比较关注的是java类中每个方法的反汇编中的指令 *** 作过程,这些指令都是顺序执行的,可以参考官方文档查看每个指令的含义。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)