前言:
现在同学们对“oracleclsn0007”大致比较关切,姐妹们都需要剖析一些“oracleclsn0007”的相关内容。那么小编也在网上网罗了一些有关“oracleclsn0007””的相关内容,希望我们能喜欢,朋友们快快来学习一下吧!上篇JVM系列-5.认识class文件介绍了class文件基本结构,这里我们用javap指令看看字节码文件的内容。
首先,介绍下javap指令,javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。当然这些信息中,有些信息(如本地变量表、指令和代码行偏移量映射表、常量池中方法的参数名称等等)需要在使用javac编译成class文件时,指定参数才能输出,比如,你直接javac xx.java,就不会再生成对应的局部变量表等信息,如果你使用javac -g xx.java就可以生成所有相关信息了。如果你使用的eclipse,则默认情况下,eclipse在编译时会帮你生成局部变量表、指令和代码行偏移量映射表等信息的。
通过反编译生成的汇编代码,我们可以深入的了解java代码的工作机制。比如我们可以查看i++;这行代码实际运行时是先获取变量i的值,然后将这个值加1,最后再将加1后的值赋值给变量i。通过局部变量表,我们可以查看局部变量的作用域范围、所在槽位等信息,甚至可以看到槽位复用等信息。
javap的用法格式:
javap <options> <classes>
其中classes就是你要反编译的class文件。在命令行中直接输入javap或javap -help可以看到javap的options有如下选项:
apple@hzdliuying6 ~ % 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> 覆盖引导类文件的位置
一般常用的是-v -l -c三个选项。
javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。
javap -l 会输出行号和本地变量表信息。
javap -c 会对当前class字节码进行反编译生成汇编代码。
查看汇编代码时,需要知道里面的jvm指令,可以参考官方文档:
另外通过jclasslib工具也可以看到上面这些信息,而且是可视化的,效果更好一些。
javap测试及内容详解
前面已经介绍过javap输出的内容有哪些,东西比较多,这里主要介绍其中code区(汇编指令)、局部变
量表和代码行偏移映射三个部分。
如果需要分析更多的信息,可以使用javap -v进行查看。
另外,为了更方便理解,所有汇编指令不单拎出来讲解,而是在反汇编代码中以注释的方式讲解。
下面用之前的TestJVM.class测试一下,代码如下:
package jvm;public class TestJVM { public static void main(String[] args) { String str = System.getProperty("str"); if (str == null) { System.out.println("not exist"); } else { System.out.println(str); } }}
16进制查看:
//cafe babe是魔数 0000是副版本号 0034是父版本号,对应十进制52表明使用的是jdk8 //002c表示常量池计数器,10进制为44,当然可以继续分析下去,//比如0a00中oa是常量池项cp_info中的tag值,10表示类中方法,//但是这么看太累了,所以我们下面是用javap指令辅助查看cafe babe 0000 0034 002c 0a00 0800 1a08 0014 0a00 1b00 1c09 001b 001d 0800 1e0a001f 0020 0700 2107 0022 0100 063c 696e6974 3e01 0003 2829 5601 0004 436f 64650100 0f4c 696e 654e 756d 6265 7254 61626c65 0100 124c 6f63 616c 5661 7269 61626c65 5461 626c 6501 0004 7468 6973 01000d4c 6a76 6d2f 5465 7374 4a56 4d3b 0100046d 6169 6e01 0016 285b 4c6a 6176 612f6c61 6e67 2f53 7472 696e 673b 2956 01000461 7267 7301 0013 5b4c 6a61 7661 2f6c616e 672f 5374 7269 6e67 3b01 0003 73747201 0012 4c6a 6176 612f 6c61 6e67 2f537472 696e 673b 0100 0d53 7461 636b 4d617054 6162 6c65 0700 2301 000a 536f 75726365 4669 6c65 0100 0c54 6573 744a 564d2e6a 6176 610c 0009 000a 0700 240c 00250026 0c00 2700 2801 0009 6e6f 7420 65786973 7407 0029 0c00 2a00 2b01 000b 6a766d2f 5465 7374 4a56 4d01 0010 6a61 76612f6c 616e 672f 4f62 6a65 6374 0100 106a6176 612f 6c61 6e67 2f53 7472 696e 67010010 6a61 7661 2f6c 616e 672f 5379 7374656d 0100 0b67 6574 5072 6f70 6572 74790100 2628 4c6a 6176 612f 6c61 6e67 2f537472 696e 673b 294c 6a61 7661 2f6c 616e672f 5374 7269 6e67 3b01 0003 6f75 74010015 4c6a 6176 612f 696f 2f50 7269 6e745374 7265 616d 3b01 0013 6a61 7661 2f696f2f 5072 696e 7453 7472 6561 6d01 00077072 696e 746c 6e01 0015 284c 6a61 76612f6c 616e 672f 5374 7269 6e67 3b29 56002100 0700 0800 0000 0000 0200 0100 09000a00 0100 0b00 0000 2f00 0100 0100 0000052a b700 01b1 0000 0002 000c 0000 00060001 0000 0003 000d 0000 000c 0001 00000005 000e 000f 0000 0009 0010 0011 0001000b 0000 0070 0002 0002 0000 001d 1202b800 034c 2bc7 000e b200 0412 05b6 0006a700 0ab2 0004 2bb6 0006 b100 0000 03000c00 0000 1600 0500 0000 0500 0600 06000a00 0700 1500 0900 1c00 0b00 0d00 00001600 0200 0000 1d00 1200 1300 0000 06001700 1400 1500 0100 1600 0000 0900 02fc0015 0700 1706 0001 0018 0000 0002 0019
使用javap -c -l TestJVM.class之后结果:
Compiled from "TestJVM.java" //编译自TestJVM.javapublic class jvm.TestJVM { //默认的构造方法,在构造方法执行时主要完成一些初始化操作,包括一些成员变量的初始化赋值 等操作 public jvm.TestJVM(); Code: //从本地变量表中加载索引为0(这里0对应下面本地变量表中的0)的变量的值,也即this的引用,压入栈 0: aload_0 //出栈,调用java/lang/Object."<init>":()V 初始化 对象, //就是this指定的对象的<init>方法完成初始化 1: invokespecial #1 //Method java/lang/Object."<init>":()V 4: return //指令与代码行数的偏移对应关系,每一行第一个数字对应代码行数, //第二个数字对应前面code中指 令前面的数字 LineNumberTable: line 3: 0 //局部变量表,start+length表示这个变量在字节码中的生命周期起始和结束的偏移位置 //(this生命周期从0开始,长度是5), //slot就是这个变量在局部变量表中的槽位(槽位可复用), //name就是变量名称,Signatur局部变量类型描述 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Ljvm/TestJVM; public static void main(java.lang.String[]); Code: 0: ldc #2 // String str 将常量str压入栈中 2: invokestatic #3 // Method java/lang/System.getProperty:(Ljava/lang/String;)Ljava/lang/String;调用静态方法 5: astore_1 6: aload_1 7: ifnonnull 21 //如果不空转到21 10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;获取静态对象 13: ldc #5 // String not exist 将常量“not exist”压入栈中 15: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V invokevirtual指令表示基于 类调用方法 18: goto 28 //转到28返回 21: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 同上 24: aload_1 //从局部变量表中取出str变量 25: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 同上 28: return LineNumberTable: line 5: 0 line 6: 6 line 7: 10 line 9: 21 line 11: 28 LocalVariableTable: Start Length Slot Name Signature 0 29 0 args [Ljava/lang/String; 6 23 1 str Ljava/lang/String;}
使用javap -v TestJVM.class查看结果,可以看到-v方式展示的更详尽:
Classfile /Users/tapple/Documents/doc/java/19-jvm/1/jvm/bin/jvm/TestJVM.class Last modified 2020-6-15; size 720 bytes //最近修改时间和文件大小 MD5 checksum 5096c7d2cce573b20c5f0d80d031f150 //MD5较验和,文件验证使用 Compiled from "TestJVM.java" //编译自TestJVM.javapublic class jvm.TestJVM //类名 minor version: 0 //副版本号 major version: 52 //主版本号 flags: ACC_PUBLIC, ACC_SUPER //访问标志Constant pool: //常量池 #1 = Methodref #8.#26 // java/lang/Object."<init>":()V #2 = String #20 // str #3 = Methodref #27.#28 // java/lang/System.getProperty:(Ljava/lang/String;)Ljava/lang/String; #4 = Fieldref #27.#29 // java/lang/System.out:Ljava/io/PrintStream; #5 = String #30 // not exist #6 = Methodref #31.#32 // java/io/PrintStream.println:(Ljava/lang/String;)V #7 = Class #33 // jvm/TestJVM #8 = Class #34 // java/lang/Object #9 = Utf8 <init> #10 = Utf8 ()V #11 = Utf8 Code #12 = Utf8 LineNumberTable #13 = Utf8 LocalVariableTable #14 = Utf8 this #15 = Utf8 Ljvm/TestJVM; #16 = Utf8 main #17 = Utf8 ([Ljava/lang/String;)V #18 = Utf8 args #19 = Utf8 [Ljava/lang/String; #20 = Utf8 str #21 = Utf8 Ljava/lang/String; #22 = Utf8 StackMapTable #23 = Class #35 // java/lang/String #24 = Utf8 SourceFile #25 = Utf8 TestJVM.java #26 = NameAndType #9:#10 // "<init>":()V #27 = Class #36 // java/lang/System #28 = NameAndType #37:#38 // getProperty:(Ljava/lang/String;)Ljava/lang/String; #29 = NameAndType #39:#40 // out:Ljava/io/PrintStream; #30 = Utf8 not exist #31 = Class #41 // java/io/PrintStream #32 = NameAndType #42:#43 // println:(Ljava/lang/String;)V #33 = Utf8 jvm/TestJVM #34 = Utf8 java/lang/Object #35 = Utf8 java/lang/String #36 = Utf8 java/lang/System #37 = Utf8 getProperty #38 = Utf8 (Ljava/lang/String;)Ljava/lang/String; #39 = Utf8 out #40 = Utf8 Ljava/io/PrintStream; #41 = Utf8 java/io/PrintStream #42 = Utf8 println #43 = Utf8 (Ljava/lang/String;)V{ public jvm.TestJVM(); //构造方法 descriptor: ()V //方法参数及返回描述 flags: ACC_PUBLIC //访问控制标志 Code: //以下类似-c -l查询的内容 stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Ljvm/TestJVM; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: ldc #2 // String str 2: invokestatic #3 // Method java/lang/System.getProperty:(Ljava/lang/String;)Ljava/lang/String; 5: astore_1 6: aload_1 7: ifnonnull 21 10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 13: ldc #5 // String not exist 15: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 18: goto 28 21: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 24: aload_1 25: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 28: return LineNumberTable: line 5: 0 line 6: 6 line 7: 10 line 9: 21 line 11: 28 LocalVariableTable: Start Length Slot Name Signature 0 29 0 args [Ljava/lang/String; 6 23 1 str Ljava/lang/String; StackMapTable: number_of_entries = 2 frame_type = 252 /* append */ offset_delta = 21 locals = [ class java/lang/String ] frame_type = 6 /* same */}
以上是javap指令简单介绍,下篇分析常量池部分
标签: #oracleclsn0007