深入理解JVM-字节码 ╰+攻爆jí腚メ 2021-09-29 23:34 358阅读 0赞 ### 字节码 ### * Class文件 * Java字节码整体结构 * Java字节码结构 * 常量池中数据类型的结构总表 * 访问标志 * 字段表集合 * 方法表 * * 方法的属性结构 * JVM规范预定义的attribute * Code结构 * 附加属性 * 代码示例 * * 示例1 * 示例2 * 示例3 # Class文件 # > Java源程序被编译器编译后称为class文件,而Class文件则由自己的格式,其采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构只有两种数据类型,无符号数和表。正是因为class文件拥有自己的格式才使得Java程序可以跨平台执行。 # Java字节码整体结构 # <table> <thead> <tr> <th>字节数</th> <th>名称</th> <th>含义</th> </tr> </thead> <tbody> <tr> <td>4</td> <td>Magic Number</td> <td>魔数,值为0xCAFEBABE</td> </tr> <tr> <td>2+2</td> <td>Version</td> <td>包括minor version和major version</td> </tr> <tr> <td>2 + n</td> <td>Constant Pool</td> <td>字符串常量池</td> </tr> <tr> <td>2</td> <td>Access Flags</td> <td>权限</td> </tr> <tr> <td>2</td> <td>This Class Name</td> <td>当前类的全限定类名,指向常量池</td> </tr> <tr> <td>2</td> <td>Super Class Name</td> <td>父类的全限定类名,指向常量池</td> </tr> <tr> <td>2+n</td> <td>Interfaces</td> <td>所实现的接口以及接口表</td> </tr> <tr> <td>2+n</td> <td>Fields</td> <td>拥有的字段以及字段表</td> </tr> <tr> <td>2+n</td> <td>Methods</td> <td>拥有的方法以及方法表</td> </tr> <tr> <td>2+n</td> <td>Attributes</td> <td>拥有的属性以及属性表</td> </tr> </tbody> </table> ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70] ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 1] # Java字节码结构 # Class字节码中有两种数据类型: * 字节数据直接量:这是基本的数据类型。共分为u1、u2、u4、u8四种,分别代表连续的一个字节、2个字节、4个字节、8个字节组成的整体数据。 * 表(数组):表是由多个基本数据或其他表,按照既定顺序组成的大的数据集合。表是有结构的,它的结构体现在:组成表的成分所在的位置和顺序都是已经严格定义好的。 # 常量池中数据类型的结构总表 # ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 2] 上面的表中描述了11中常用的数据类型,jdk1.7之后又增加了三种,CONSTANT\_MethodHandle\_info、CONSTANT\_MethodType\_info以及CONSTANT\_MethodDynamic\_info。 # 访问标志 # ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 3]如果一个类是public以及final的,那么其标志值为分别对应的标志值相或,所以其标志值为0x0011。 # 字段表集合 # 字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部的局部变量。 fields\_count: u2 (表示字段的个数) ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 4] # 方法表 # method\_count:u2 (表示方法的个数) ![在这里插入图片描述][20190722095753976.png] ## 方法的属性结构 ## 方法中的每个属性都是一个attribute\_info结构 ![在这里插入图片描述][20190722095948858.png] ## JVM规范预定义的attribute ## * JVM预定义了部分attribute,但是编译器自己也可以实现自己的attribute写入class文件里,供运行时使用。 * 不同的attribute通过attribute\_name\_index来区分。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 5] ## Code结构 ## Code attribute的作用是保存该方法的结构 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 6] * attribute\_length 表示attribute所包含的字节数,不包含attribute\_name\_index和attribute\_length字段。 * max\_stack表示这个方法运行的任何时刻所能达到的操作数栈的最大深度 * max\_locals表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量 * code\_length表示该方法所包含的字节码的字节数以及具体的指令码 * 具体字节码即是该方法被调用时,虚拟机所指向的字节码 * exception\_table,异常表,这里存放的是处理异常的信息 * 每个exception\_table表项由start\_pc,end\_pc,handle\_pc,catch\_type组成 * start\_pc和end\_pc表示在code数组中的从start\_pc和end\_pc处(包含start\_pc,不包含end\_pc)的指令抛出的异常由这个表项来处理 * handler\_pc表示处理异常的代码的开始处。catch\_type表示会被处理的异常类型,它指向常量池里的一个异常类。当catch\_type为0时,表示处理所有异常 ## 附加属性 ## 方法的附加属性: * LineNumberTable:这个属性用来表示code数组·中的字节码和Java代码行数之间的关系。这个属性可以用来在调试的时候定位代码执行的行数。 # 代码示例 # ## 示例1 ## public class MyTest1 { private int a = 1; public int getA() { return a; } public void setA(int a) { this.a = a; } } E:\IdeaProjects\jvm_lecture\target\classes>javap -verbose com/wangzhao/jvm/bytecode/MyTest1 Classfile /E:/IdeaProjects/jvm_lecture/target/classes/com/wangzhao/jvm/bytecode/MyTest1.class Last modified 2019-7-21; size 497 bytes MD5 checksum 5fea812ebd710c6be8d438b7e0959a03 Compiled from "MyTest1.java" public class com.wangzhao.jvm.bytecode.MyTest1 minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #4.#20 // java/lang/Object."<init>":()V #2 = Fieldref #3.#21 // com/wangzhao/jvm/bytecode/MyTest1.a:I #3 = Class #22 // com/wangzhao/jvm/bytecode/MyTest1 #4 = Class #23 // java/lang/Object #5 = Utf8 a #6 = Utf8 I #7 = Utf8 <init> #8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lcom/wangzhao/jvm/bytecode/MyTest1; #14 = Utf8 getA #15 = Utf8 ()I #16 = Utf8 setA #17 = Utf8 (I)V #17 = Utf8 (I)V #18 = Utf8 SourceFile #19 = Utf8 MyTest1.java #20 = NameAndType #7:#8 // "<init>":()V #21 = NameAndType #5:#6 // a:I #22 = Utf8 com/wangzhao/jvm/bytecode/MyTest1 #23 = Utf8 java/lang/Object { public com.wangzhao.jvm.bytecode.MyTest1(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_1 6: putfield #2 // Field a:I 9: return LineNumberTable: line 7: 0 line 10: 4 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcom/wangzhao/jvm/bytecode/MyTest1; public int getA(); descriptor: ()I flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #2 // Field a:I 4: ireturn LineNumberTable: line 13: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/wangzhao/jvm/bytecode/MyTest1; public void setA(int); descriptor: (I)V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: iload_1 2: putfield #2 // Field a:I 5: return LineNumberTable: line 18: 0 line 19: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/wangzhao/jvm/bytecode/MyTest1; 0 6 1 a I } SourceFile: "MyTest1.java" 通过javap命令,我们对MyTest1的class文件解析可得到如下结果: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 7] ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 8]![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 9] 通过常量池,我们可以找到这个类拥有哪些字段,方法以及父类和接口的全限定类名。需要注意的是,常量池中的索引是从1开始,并没有使用0是因为0表示不引用任何一个常量。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 10] 可以看到==对于类中实例变量的赋值,是在构造方法中完成的。==同时编译器为我们自动增加了一个构造方法。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 11] 对于getA()方法,我们并没有传入参数,但是参数个数却为1,这是因为对于Java类中的每一个**实例方法**(非static)方法,其在编译后所生成的字节码当中,方法参数的数量总是会比源代码中方法参数的数量多一个(this),它位于方法的参数的第一个参数位置处;这样,我们就可以在java的实例方法中使用this来去访问当前对象的属性以及其他方法。 ## 示例2 ## public class MyTest2 { String str = "Welcome"; private int x = 5; public static Integer in = 10; public static void main(String[] args) { MyTest2 myTest2 = new MyTest2(); myTest2.setX(8); in = 20; } public void setX(int x) { this.x = x; } private static void test(String str){ synchronized (str){ System.out.println("hello world"); } } } ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 12] 可以看到synchronized与两个指令相关联,分别为minitorenter以及monitorexit。 minortorenter的作用是:执行该指令的线程尝试获取相关对象的监视器的所有权,如果监视器的计数为0,则线程进入监视器并将其计数设置为1。如果该线程已经拥有该对象的所有权,则监视器的计数加1(可重入)。而其他线程将阻塞,直到该对象的监视器计数为1。 minortorexit的作用是:拥有当前对象监视器所有权的线程递减关联对象的监视器计数,直到为0,当前线程退出监视器。 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 13] 对于静态的字段,编译器会生成一个静态构造代码块,完成静态成员变量的赋值。 ## 示例3 ## public class MyTest3 { public void test(){ try { InputStream is = new FileInputStream("test.txt"); ServerSocket serverSocket = new ServerSocket(9999); }catch (FileNotFoundException ex){ }catch (IOException ex){ }catch (Exception ex){ }finally { System.out.println("finally"); } } } ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 14] ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 15] 可以看到对于异常的处理流程是通过异常表,罗列出所有可能的路径。 当异常处理存在finally语句块时,现代化的JVM采取的处理方式是将finally语句块的字节码拼接到每一个catch块后面,换句话说,程序中存在多少个catch块,就会在每一个catch块后面重复多少个finally语句块的字节码。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70]: /images/20210812/d72daa28cc62423b9405c86b4431fc13.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 1]: /images/20210812/6322ac1e420743119a3c09aae7019bd0.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 2]: /images/20210812/6e04c13df883479db1895916dc76231d.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 3]: /images/20210812/ac09616f495741d4a2cdd0ff8ed4669b.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 4]: /images/20210812/8390d244161543a8b8e4afcc58f83b36.png [20190722095753976.png]: /images/20210812/a736413622624078965114c505560496.png [20190722095948858.png]: /images/20210812/b58961b02c8040268b7ab7d6a7674132.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 5]: /images/20210812/f5ba0c4015884929bed17260bcffb498.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 6]: /images/20210812/eb4b09b8f46e43579392203aaaa3fa39.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 7]: /images/20210812/8720e5b127444ea3862eb802fdfc36ad.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 8]: https://img-blog.csdnimg.cn/201907221038284.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18=,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 9]: /images/20210812/525fd6a0af27434eb89f4536613f6deb.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 10]: /images/20210812/0131eea8a9cd4084ac7e8c946e6bab24.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 11]: /images/20210812/645e822c00714c6495b0bba11b225cde.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 12]: /images/20210812/fa128cdbf64c443891c71fe59da4d38b.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 13]: /images/20210812/23bbbfb3afd14b7c8665d85a079826d6.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 14]: /images/20210812/5ad556303c5d4ffb835d3869d40db125.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dhbmdfemhhb18_size_16_color_FFFFFF_t_70 15]: /images/20210812/bb7754cd324f4f5a82b18e695c56b280.png
还没有评论,来说两句吧...