让建站和SEO变得简单

让不懂建站的用户快速建站,让会建站的提高建站效率!

一篇带给你JVM 字节码解析经过
迪士尼彩票官网

你的位置:迪士尼彩票 > 迪士尼彩票官网 >

一篇带给你JVM 字节码解析经过

发布日期:2022-05-15 14:31    点击次数:136

详细

详细本文主如果基于 .class 文献,进行分析 .class 文献的内容。

这部分个人认为主如果属于有计划机构拓展的内容,宇宙不错一齐来学习一下 Java 字节码的有计划结构以及感受一下有计划者的有计划。

class 类文献结构

Java 提供 javap 号令不错分析字节码文献,咱们不错使用 javap -verbose 号令分析一个字节码文献时, 将会分析该字节码文献的魔数、版块号、常量池、类信息、类的构造顺序、类中的顺序信息、类变量与成员变量等信息。

一个简便的 Java 代码

public class TestClass {      private int m;      public int inc() {        return ++m;     } } 

下图披露的是 Java 代码编译后 .class 文献的十六进制信息

bytecode_十六进制.png

为了便捷对比我执行一下 javap -v TestClass

Classfile /../../TestClass.class   Last modified 2021-2-6; size 306 bytes   MD5 checksum eeba40cc40cc28ef4d416ff70d901561   Compiled from "TestClass.java" public class cn.edu.cqvie.jvm.bytecode.TestClass   minor version: 0   major version: 52   flags: ACC_PUBLIC, ACC_SUPER Constant pool:    #1 = Methodref          #4.#15         // java/lang/Object."<init>":()V    #2 = Fieldref           #3.#16         // cn/edu/cqvie/jvm/bytecode/TestClass.m:I    #3 = Class              #17            // cn/edu/cqvie/jvm/bytecode/TestClass    #4 = Class              #18            // java/lang/Object    #5 = Utf8               m    #6 = Utf8               I    #7 = Utf8               <init>    #8 = Utf8               ()V    #9 = Utf8               Code   #10 = Utf8               LineNumberTable   #11 = Utf8               inc   #12 = Utf8               ()I   #13 = Utf8               SourceFile   #14 = Utf8               TestClass.java   #15 = NameAndType        #7:#8          // "<init>":()V   #16 = NameAndType        #5:#6          // m:I   #17 = Utf8               cn/edu/cqvie/jvm/bytecode/TestClass   #18 = Utf8               java/lang/Object {   public cn.edu.cqvie.jvm.bytecode.TestClass();     descriptor: ()V     flags: ACC_PUBLIC     Code:       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    public int inc();     descriptor: ()I     flags: ACC_PUBLIC     Code:       stack=3, locals=1, args_size=1          0: aload_0          1: dup          2: getfield      #2                  // Field m:I          5: iconst_1          6: iadd          7: dup_x1          8: putfield      #2                  // Field m:I         11: ireturn       LineNumberTable:         line 8: 0 } SourceFile: "TestClass.java" 
Java 字节码结构

bytecode_结构.png

1. 魔数和 Class 文献版块

魔数:通盘的.class 字节码文献的前4个字节都是魔数,魔数为固定值: 0xCAFEBABE

版块信息,魔数之后的4个字节是版块信息,前两个字节暗示 minor version (次版块号), 后2个字节暗示major version (主版块号)。这里的版块号 00 00 00 34 换算成十进制表, 暗示次版块号为0, 主版块号为 52. 是以该文献的版块号为 1.8.0。不错通过 java -version 来考据这少许。

➜  ~ java -version java version "1.8.0_281" Java(TM) SE Runtime Environment (build 1.8.0_281-b09) Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode) 
2. 常量池

常量池 (constant pool): 2+N个字节 紧接着主版块号之后的便是常量池进口。一个java 类中界说的许多信息都是由常量池来描摹的,不错将常量池看作是 Class 文献的资源仓库,比如说Java类中变量的顺序与变量信息,都是存储在常量池中。常量池中主要存储2类常量:字面量与记号援用。

字面量, 如字符串文本,java 中声明为final 的常量值等。 记号援用, 如类和接口的全局截止名, 字段的称呼和描摹符,顺序的称呼和描摹符等。

常量池的总体结构:Java类所对应的常量池主要由常量池(常量表)的数量与常量池数组这两部分共同组成。常量池中常量数量紧随着在主版块号后头,占据2字节: 常量池长度 比如这里咱们的十六进制便是 00 13 代表有 18 个常量。常量数组则紧随着常量池数量之后。常量池数组与一般数组不同的是, 常量池数组中不同的元素的类型,结构都是不同的。长度虽然也就不同;然而, 一种元素的第一种元素的第一个数据都是一个u1类型, 该字节是一个标志位,占据1个字节。JVM在解析常量池时,会更具这个u1 类型来得到元素的具体类型。值得看重的是:常量池中元素的个数 = 常量池数 -1 (其中0暂时不适用), 指标是餍足某些常量池索引值的数据在特定情况下需要抒发【不援用任何一个常量池】的含义:根柢原因在于,索引为0亦然一个常量(保留常量),只不外他不位于常量表中。这个常量就对应null值, 是以常量池的索引是从1出手而非0出手。

上头表中描摹了11种数据类型的机构, 其着实jdk1.7之后又加多了3种(CONSTANT_MethodHandle_info, CONSTANT_MethodType_info 以及CONSTANT_InvokeDynami_info)。这么一共14种。

在JVM圭表中, 每个变量/字段都有描摹信息, 描摹信息主要的作用是描摹字段的数据类型、顺序的参数列表(包括数量、类型、律例)与复返值。把柄描摹符 端正, 基本数据类型和代表无复返值的的void 类型都用一个大写字符来暗示, 对象类型则使用字符L加对象的全截止称呼来暗示。为了压缩字节码文献的体积 关于基本数据类型,JVM都只使用一个大写字母来暗示,如下所示:B-byte, C-char, D-double, F-float, I-int, J-long, S-short, Z-boolean , V -void L -暗示对象类型,如: Ljava/lang/String;

关于数组类型来说,每一个维度使用一个前置的 [来暗示, 如int[] 被标记为 [I , String[][]被暗示为 [[java/lang/String;

用描摹符描摹顺序时, 按照先参数列表, 后复返值的律例来描摹. 参数列表按照参数的严格律例放在一组()内, 如顺序: String getRealnameByIdNickname(int id, String name)的描摹符为: (I, Ljava/lang/String;) Ljava/lang/String

Class 字节码中有两种数据类型 字节数据平直量:这是基本的数据类型。共细分为u1、u2、u4、u8四种,分别代表采集的1个字节、2个字节、4个字节、8个字节组成的全体数据。 表(数组):暗示由多个基本数据或其他表,按照既定律例组成的大的数据聚积。表是有结构的 。它的结构体面前:组成表的因素方位的位置回绝序都是 严格界说好的。

常量池常量

0A 00 04 00 0F method_info 00 04 指向常量池中的常量的位置, 00 0F 指向的是 15 的位置。 09 00 03 00 10 field_info 00 03 指向 3 的位置 00 10 (16 位置) 07 00 11 class info 00 11 (17 位置) 07 00 12 class info 00 12 (18 位置) 01 00 01 6D utf8 长度为 1 内容为 6D 暗示内容相通为 10 进制为 109 相通为 ASCII 码终末的成果为 m 01 00 01 49 utf8 长度为 1 内容为 I 01 00 06 3C 69 6E 69 74 3E utf8 长度为 8 内容为 01 00 03 28 29 56 utf8 长度为 3 内容为:()V 01 00 04 43 6F 64 65 utf8长度为 4 内容为 Code 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 utf8 长度为 15 内容为 LineNumberTable 01 00 03 69 6E 63 utf8 长度为3 内容为 inc 01 00 03 28 29 49 长度为 3 内容为 ()I 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 长度为 10 内容为 SourceFile 01 00 0E 54 65 73 74 43 6C 61 73 73 2E 6A 61 76 61 长度为 14 内容为 TestClass.java 0C 00 07 00 08 NameAndType 内容 指向 7 位置。00 08 (8 位置) 0C 00 05 00 06 NameAndType 内容 指向 5 位置。00 06 (6 位置) 01 00 23 63 6E 2F 65 64 75 2F 63 71 76 69 65 2F 6A 76 6D 2F 62 79 74 65 63 6F 64 65 2F 54 65 73 74 43 6C 61 73 73 长度 35 内容 cn/edu/cqvie/jvm/bytecode/TestClass 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 长度 16 内容 java/lang/Object 3. 拜谒标志

Access_Flag 拜谒标志 拜谒标志信息包括该Class文献时类和接口是否被界说成了public,是否是 abstract, 如果是类,是否被声名为成final。通过扇面的源代码。

0x 00 21: 暗示是0x0020 和0x0001的并集, 暗示 ACC_PUBLIC 与 ACC_SUPER

bytecode_拜谒标志.png

4. 类索引、父类索引

00 03 类名, 03 常量池位置 cn/edu/cqvie/jvm/bytecode/TestClass

00 04 父类名. 04 常量池位置 java/lang/Object

00 00 接口个数, 0 个, 接口个数最多 65535

5. 字段表聚积

00 01 字段的个数, 这里有一个

bytecode_字段表.png

字段表结构
field_info {   u2 access_flags; 00 02 Fieldref   u2 name_index; 00 05 (暗示字段称呼在常量池中的索引位置) m   u2 descriptor_index; 00 06 (描摹符索引) I    u2 attributes_count; 00 00   attribute_info attributes[attributes_count]  } 
6. 顺序表聚积

00 02 暗示有两个顺序

bytecode_顺序表.png

顺序表结构

method_info {   u2 access_flags; 00 01  Methodref   u2 name_index;  // 00 07  <init>   u2 descriptor_index; 00 08 // ()V   u2 attributes_count; 00 01 // 属性结构   attributes_info attributes[attributes_count] } 

顺序属性结构

attribute_info {   u2 attribute_name_index; 00 09 // Code   u4 attribute_length; 00 00 00 1D 长度 29   u1 info[attribute_length];     ...  } 
7. 属性表聚积

00 09 00 00 1D 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0A 00 00 00 06 00 01 00 00 00 03 00

"Code" 暗示底下是执行代码

JVM 预界说了一部分的attribute, 然而编译器我方也不错收场我方的attribute 写入class文献中, 供运行时使用。不同的attribute 通过attribute_name_index 来差异。

JVM 圭表预界说的attribute

Code 结构

Code attribute 的作用是保存该放的的结构,如所对应的字节码

Code_attribute {     u2 attribute_name_index;  // 00 09 ==> Code     u4 attribute_length; // 00 00 00 1D ==> 29     u2 max_stack;   //  00 01 栈深度为 1 (栈帧中操作数栈的深度)     u4 code_length;   // 00 00 00 05 教唆的长度是若干     u1 code[code_lenght]; // 2A B7 00 01 B1 其中 2A          // 2A aload_0     // B7 invokespecial     // 00 #1     // 01 <java/lang/Object.<init>>     // B1 return          // 0 aload_0   // 1 invokespecial #1 <java/lang/Object.<init>>   // 4 return     u2 exception_table_length; // 00 00      {         u2 start_pc;         u2 end_pc;         u2 handler_pc;         u2 catch_type;     } exception_table[exception_table_lenght];     u2 attributes_count; // 00 01     attribute_info attributes[attributes_count]; } 

attribute_length 暗示 attribute 所包含的字节数, 不包含attribute_name_index 和 attribute_length 字段。

max_stack 暗示这个顺序运行的任何时刻所能达到的操作数栈的最大深度。

max_locals 暗示顺序执行工夫创建的局部变量的数量,包含用来暗示传入的参数的局部变量。

code_length 暗示该顺序所包含的字节码的字节数以及具体的教唆码。

具体字节码即是该顺序被调用时,诬捏机所执行的字节码。

exception_table, 这里存放的是处置终点信息。

每个 exception_table, 这里存放的是处置终点的信息。

每个 exception_table 表项由start_pc , end_pc, handler_pc, catch_type 组成。 start_pc 和 end_pc 暗示在code数组中的从 start_pc 到 end_pc 处(包含start_pc, 不包含end_pc)的教唆抛出的终点会由 这个表项来处置。 handler_pc 暗示处置终点的代码的出手处。catch_type 暗示会被处置的终点类型, 它指向常量池里的一个终点类。当catch_type为0时, 暗示处置通盘的终点。 LineNumberTable 的结构
LineNumberTable_attribute {     u2 attribute_name_index; //00 0A 常量池10号位置  LineNumberTable     u4 attribute_lenght;  // 00 00 00 06 一共 6个长度     u2 line_number_table_length;  // 00 01 映射的对数  1 对     line_number_info {         u2 start_pc; // 教唆行号  00 00          u2 line_number; // 源码调试  00 03     }     line_number_table[line_number_table_length];  } 

局部变量表 LocalVariableTable

LocalVariableTable_attribute {     u2 attribute_name_index;      u4 attribute_lenght;     u2 local_variable_table_length;      {         u2 start_pc;         u2 line_number;         u2 name_index;         u2 descriptor_index;          u2 index;     } } 
回想 构造顺序中会启动化成员属性的默许值,如果我方收场了默许的构造顺序, 已经照旧在构造顺序中赋值,这便是对教唆的重排序。 如果多个构造顺序那么每个构造顺序中都有启动化成员变量的属性,来保险每个构造顺序启动化的时间都会执行到属性的启动化经过。 如果构造顺序中有执行语句, 那么会先执行赋值信息, 然后在执行自界说的执行代码。 关于Java每一个实例顺序(非静态顺序), 其在编译青年景的字节码中比实质顺序多一个参数, 它位于顺序的第一个参数位置. 咱们就不错在现时列法中 的this去拜谒现时对象中的this这个操作是在Javac 编译器在编译工夫将this的拜谒相通为对辽远实例顺序的参数拜谒,接下来在运行工夫, 由JVM的调用实例顺序时, 自动向实例顺序中传入该this参数, 是以在实例顺序的局部变量表中, 至少会一个指向现时对象的局部变量。 字节码关于处置终点的边幅: 挽救收受终点表的边幅来对终点处置。 在Jdk1.4.2之前的版块中, 并不是使用终点表的边幅来对终点进行处置的,而是收受特定的教唆边幅。 当终点处置存在finally 语句块时,当代化的JVM才去的处置边幅将finally语句块的我方拼接到每一个catch块后头, 换句话说,能力中中存在多个catch块,就会在每一个catch块后头近似若干个finally 的语句块字节码。

 



友情链接:

Powered by 迪士尼彩票 @2013-2022 RSS地图 HTML地图

Copyright 365建站 © 2013-2021 365建站器 版权所有