JVM学习笔记之class文件结构【七】

一、概念

1.1 无符号数:

以 u1、u2、u3、u4、u8 代表 1 个字节,2 个字节、4 个字节、8 个字节的无符号数。无符号数可以描述数字,索引引用、数量值和按照 UTF-8 编码构成的字符串值。

1.2 表

  • 表是由多个无符号数或其他表作为数据项构成的复合的数据结构,所有表都习惯性的以”_info”结尾。表用于表示有层次关系的复合结构的数据,整个 Class 文件本质上是一张表

1.3 class 文件组成

ClassFile {
    u4             magic;  //魔数, 用于识别class文件格式
    u2             minor_version;//次版本号
    u2             major_version;//主版本号
    u2             constant_pool_count;  //常量池计数器
    cp_info        constant_pool[constant_pool_count-1]; //常量池
    u2             access_flags;//访问标志
    u2             this_class;//类索引
    u2             super_class;//父类索引
    u2             interfaces_count;//接口计数器
    u2             interfaces[interfaces_count];//接口索引集合
    u2             fields_count;//字段计数器
    field_info     fields[fields_count];//字段表集合
    u2             methods_count;//方法计数器
    method_info    methods[methods_count];//方法表
    u2             attributes_count; //属性计数器
    attribute_info attributes[attributes_count];附加属性表
}

1.4 魔数

每个 Class 文件的头 4 个字节被称为魔数(Magic Number),它的唯一作用是确定这个文件是否能被虚拟机接受的 Class 文件。它的值是 0xCAFEBABE (咖啡宝贝),非常容易记忆。

1.5 版本号

紧接着的字节是次版本号(minor_version)和主版本号(major_version),Java 的版本号从 45 开始,Java1.1 之后的 JDK 大版本发布主版本号向上加一(Java1.0~Java1.1 使用了 45.0~45.3 的版本号)。注意高版本的 JDK 能向下兼容 以前的 Class 文件,但不能运行以后版本的 Class 文件。

1.6 常量池

常量池可以理解为 Class 文件的资源仓库,

主要存放:

  • 字面量(Literal)
  • 符号引用(Symbolic References)
  • 类和接口的全限定名(Full Qualified Name)
  • 字段的名称描述符(Descriptor)
  • 方法的名称和描述符类型 标识 描述 CONSTANT_Class 7 类或接口的符号引用 CONSTANT_Fieldref 9 字段的符号引用 CONSTANT_Methodref 10 方法的符号引用 CONSTANT_InterfaceMethodref 11 接口中方法的符号引用 CONSTANT_String 8 字符串类型字面量 CONSTANT_Integer 3 整型字面量 CONSTANT_Float 4 浮点型字面量 CONSTANT_Long 5 长整型字面量 CONSTANT_Double 6 双精度浮点型字面量 CONSTANT_NameAndType 12 字段或方法的部分符号引用 CONSTANT_Utf8 1 UTF-8 编码字符串 CONSTANT_MethodHandle 15 标识方法句柄 CONSTANT_MethodType 16 标识方法类型 CONSTANT_InvokeDynamic 18 动态方法调用点

1.7 访问标识(access_flags)

用于识别类和接口层次的访问信息

Flag Name Value Interpretation ACC_PUBLIC

0x0001 是否为被声明为 public ,可以被其他外部包中访问 ACC_FINAL

0x0010 是否被声明 final,不能派生子类 ACC_SUPER

0x0020 Treat superclass methods specially when invoked by the
invokespecial

instruction. ACC_INTERFACE

0x0200 标识一个接口 ACC_ABSTRACT

0x0400 声明 abstract,抽象类,不能实例化 ACC_SYNTHETIC

0x1000 声明 synthetic; 标识这个类并非有用户代码产生 ACC_ANNOTATION

0x2000 标识这个一个注解 ACC_ENUM

0x4000 标识这是一个枚举

1.8 类索引、父类索引和接口索引

Class 文件就是由这三项数据来确定这个类的继承关系。类索引用于确定类的全限定类名,父索引用于确定父类的全限定类名,接口索引集合用于描述类实现了那些接口。

1.9 字段表集合

字段表集合[field_info] 用于描述接口或者类中声明的变量。字段(field) 包括类变量和实例变量,但不包括方法内部声明的局部变量。

  • 字段表结构
field_info {
    u2             access_flags; //访问标识
    u2             name_index; //名称索引
    u2             descriptor_index;  //描述符索引
    u2             attributes_count;  //属性计数器
    attribute_info attributes[attributes_count];  //属性表
}
  • 字段包含的信息:
  • 作用域(public 、private、protected 修饰符)
  • static 修饰符
  • 可变性 final
  • 并发可见性 volatile
  • 可否序列化 transient
  • 字段类型 【基本数据类型(byte、char、short、int、long 、float、double、boolean)、对象、数组】
  • 字段访问标志 ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its package. ACC_PRIVATE 0x0002 声明 private; ACC_PROTECTED 0x0004 声明 protected; ACC_STATIC 0x0008 声明 static. ACC_FINAL 0x0010 声明 final; ACC_VOLATILE 0x0040 声明 volatile; ACC_TRANSIENT 0x0080 声明 transient; ACC_SYNTHETIC 0x1000 声明 synthetic; 字段是否有编译器自动产生的 ACC_ENUM 0x4000 声明字段是否是枚举

  • 简单名称:没有类型和参数修饰的方法或者字段名称,如 inc()和 m 字段的简称为 inc 和 m

  • 全限定名:com/demo/TestClass; “;”标识类的全限定名结束
  • 描述符:用于描述字段的数据类型,方法的参数列表(数量、类型、顺序)和返回值标识字符 代表类型 描述 B byte 基本类型 byte C char 基本类型 char D double 基本类型 double F float 基本类型 float I int 基本类型 int J long 基本类型 long L ClassName ; reference 对象类型,如 : Ljava/lang/Object S short 基本类型 short Z boolean j 基本类型 boolean [ reference 数组类型 ,如数组 int[] 被记录为 [I,数组 String[][]被记录为 [[java/lang/String V void 特殊类型 Void

描述符来描述方法时,按照先参数列表,后返回值的顺序描述;如:java.lang.String.toString() 描述为 () Ljava/lang/String,java.lang.String#valueOf(char[], int, int) 描述为 ([CII)Ljava/lang/String

1.10 方法表集合

方法描述采取与字段描述完全一致的方式。

  • 方法表结构
method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
  • 相关访问标识Flag Name Value Interpretation ACC_PUBLIC 0x0001 方法是否 public ACC_PRIVATE 0x0002 方法是否 private ACC_PROTECTED 0x0004 方法是否 protected; ACC_STATIC 0x0008 方法是否 static. ACC_FINAL 0x0010 方法是否 final; ACC_SYNCHRONIZED 0x0020 方法是否 synchronized; 标识同步方法 ACC_BRIDGE 0x0040 标识是否由编译器生成的桥接方法 ACC_VARARGS 0x0080 方法是否接受不定参数 ACC_NATIVE 0x0100 方法是否 native; ACC_ABSTRACT 0x0400 方法是否 abstract; ACC_STRICT 0x0800 方法是否 strictfp; ACC_SYNTHETIC 0x1000 方法是否为 synthetic;
  • 方法里定义的代码 方法里面的代码,经过编译器编译成字节指令后,存放在方法属性表集合,名为 Code 属性里。

1.11 属性表集合在

属性表(attribute_info)在 Class 文件、字段表、方法表中都可以携带自己的属性表集合,用于描述某些场景专有的信息。

  • 格式结构
attribute_info {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
}
  • 虚拟机预定义属性属性 位置 含义 class 版本 SourceFile ClassFile 记录源文件名称 45.3 InnerClasses ClassFile 内部类列表 45.3 EnclosingMethod ClassFile 仅当一个类为局部类或匿名类时才能拥有这个属性,这个属性用于标识这个类所在的外围方法 49.0 SourceDebugExtension ClassFile JDK 1.6 中新增的属性,SourccDcbugExtcnsion 属性用于在储额外的调试信息。譬如在进行 JSP 文件调试时,无法通过 Java 堆栈来定位到 JSP 文件的行号, JSR-45 规范为这些非 Java 语言编写,却需要编译成字节码并运行在 Java 虚拟机中的程序提供了一个进行调试的标准机制,使用 SourccDcbugExtcnsion 属性就可以用于存储这个标准所新加入的调试信息 49.0 BootstrapMethods ClassFile JDK1.7 新增的属性,用于保存 invokedynamic 指令引用的引导方法限定符 51.0 ConstantValue field_info final 关键字定义的常量值 45.3 Code method_info Java 代码编译成的字节码指令 45.3 Exceptions method_info 方法抛出的异常 45.3 RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations method_info JDK5 中新增的属性,作用于方法参数 RuntimeVisibleParameterAnnotations &#x5C5E;&#x6027;&#x6307;&#x660E;&#x54EA;&#x4E9B;&#x6CE8;&#x89E3;&#x662F;&#x8FD0;&#x884C;&#x65F6;&#x53EF;&#x89C1;&#xFF1B;RuntimeInvisibleAnnotations属性指明哪里注解是运行时不可见的 49.0AnnotationDefaultmethod_infoJDK1.5 中新增的属性,用于记录注解类元素的默认值 49.0MethodParametersmethod_infoMethodParameters 属性记录方法的形式参数的信息,比如方法名称。 52.0SyntheticClassFile,field_info,method_info标识方法或字段为编译器自动生成的 45.3DeprecatedClassFile,field_info,method_info被声明为 Deprecated 的方法和字段 45.3SignatureClassFile,field_info,method_info记录类,接口,构造函数,方法或字段的签名 49.0RuntimeVisibleAnnotations,RuntimeInvisibleAnnotationsClassFile,field_info,method_infoJDK5 中新增的属性,为动态注解提供支持。RuntimeVisibleAnnotations属性指明哪些注解是运行时可见;RuntimeInvisibleAnnotations属性指明哪里注解是运行时不可见的 49.0LineNumberTableCodeLineNumberTable 属性表存放方法的行号信息 45.3LocalVariableTableCodeLocalVariableTable 属性表中存放方法的局部变量信息 45.3LocalVariableTypeTableCodeJDK 1.5 中新增的屈件,它使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加 49.0StackMapTableCodeJDKL6 中新增的属性.供新的类型检查验证器 (Type Checker)检查和处理目标方法的后部变量和操作数栈所需要的类型是否匹配 50.0RuntimeVisibleTypeAnnotations,RuntimeInvisibleTypeAnnotationsClassFile,field_info,method_info,Codejdk8 新增属性<br>RuntimeVisibleTypeAnnotations:运行时可见类型注解<br>RuntimeInvisibleTypeAnnotations`:运行时不可见类型注解 52.0

  • Code 属性 Java 程序方法体中的代码经过 Javac 编译器处理后,最终成为字节码指令存储在 Code 属性内。注意并不是所有方法表都存在 Code 属性,例如,接口和抽象类中的方法就不存在 Code 属性。

  • Code 属性格式定义
Code_attribute {
    u2 attribute_name_index;  //&#x6307;&#x5411;&#x5E38;&#x91CF;CONSTANT_UTF8_info&#x7684;&#x7D22;&#x5F15;&#xFF0C;&#x5E38;&#x91CF;&#x56FA;&#x5B9A;&#x503C;&#x4E3A;Code
    u4 attribute_length;
    u2 max_stack;   //&#x64CD;&#x4F5C;&#x6570;&#x6808;
    u2 max_locals;  //&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;&#x6240;&#x9700;&#x7684;&#x5B58;&#x50A8;&#x7A7A;&#x95F4;

    //&#x5B57;&#x8282;&#x7801;&#x957F;&#x5EA6;&#xFF0C;&#x6700;&#x5927;&#x503C;&#x53EF;&#x8FBE;2^32-1, &#x4F46;&#x662F;&#x865A;&#x62DF;&#x673A;&#x9650;&#x5236;&#x4E86;&#x4E00;&#x4E2A;&#x65B9;&#x6CD5;&#x4E0D;&#x5141;&#x8BB8;&#x8D85;&#x8FC7;65535&#x6761;&#x5B57;&#x8282;&#x7801;&#x6307;&#x4EE4;
    //&#x5373;&#x4F7F;&#x7528;&#x4E86;u2 &#x7684;&#x957F;&#x5EA6;&#xFF0C;&#x8D85;&#x51FA;&#x8FD9;&#x4E2A;&#x9650;&#x5236;&#x4F1A;&#x5BFC;&#x81F4;&#x7F16;&#x8BD1;&#x5931;&#x8D25;
    u4 code_length;
    u1 code[code_length]; //&#x5B57;&#x8282;&#x7801;&#x6307;&#x4EE4;&#x7684;&#x5B50;&#x8282;&#x6D41;
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

二、字节码指令

2.1 加载和存储指令

加载和存储指令用于将数据在栈帧中的局部变量表与操作数栈之间传输。

  • 将局部变量加载到操作数栈
// i  &#x4EE3;&#x8868;&#x5BF9;int &#x64CD;&#x4F5C;
// l  &#x4EE3;&#x8868;&#x5BF9;long &#x64CD;&#x4F5C;
// f &#x4EE3;&#x8868;&#x5BF9;float &#x64CD;&#x4F5C;
// d &#x4EE3;&#x8868;&#x5BF9;double &#x64CD;&#x4F5C;
// a &#x4EE3;&#x8868;&#x5BF9;&#x5F15;&#x7528;reference &#x64CD;&#x4F5C;
// iload_<n>  &#x4EE3;&#x8868;&#x4E00;&#x7EC4;&#x6307;&#x4EE4;&#xFF0C;iload_0&#x3001;iload_1&#x3001;iload_2&#x3001;iload_3&#x7B49;&#x6307;&#x4EE4;
iload
iload_<n>
lload
lload_<n>
fload
fload_<n>
dload
dload_<n>
aload
aload_<n>
</n></n></n></n></n></n>
  • 将数值从操作数栈存储到局部变量表
istore
istore_<n>
lstore
lstore_<n>
fstore
fstore_<n>
dstore
dstore_<n>
astore
astore_<n>
</n></n></n></n></n>
  • 将常量加载到操作数栈
bipush
sipush
ldc
ldc_w
ldc2_w
aconst_null
iconst_ml
iconst_<i>
lconst_<l>
fconst_<f>
dconst_<d>
</d></f></l></i>
  • 扩充局部变量表的访问索引的指令:wide

2.2 运算指令

相关指令
  • 加法指令
iadd&#x3001;ladd&#x3001;fadd&#x3001;dadd
  • 减法指令
isub&#x3001; lsub&#x3001; fsub&#x3001; dsub
  • 乘法指令
imul&#x3001; lmul&#x3001; fmul&#x3001; dmul
  • 除法指令
idiv&#x3001; ldiv&#x3001; fdiv&#x3001; ddiv
  • 求余指令
 irem&#x3001; lrem&#x3001; frem&#x3001; drem
  • 取反指令
ineg&#x3001; lneg&#x3001; fneg&#x3001; dneg
  • 位移指令
ishl&#x3001; isbr&#x3001; iusbr&#x3001; lsbl&#x3001; lshr&#x3001; lushr
  • 按位或指令
ior&#x3001; lor
  • 按位与指令
iand&#x3001; land
  • 按位异或指令
ixor&#x3001; lxor
  • 局部变量自增指令
iinc
  • 比较指令
dcmpg&#x3001; dcmpl&#x3001; fcmpg&#x3001; fcmpl&#x3001; lcmp
注意
  • 只有当除法指令和求余指令遇到除数为零时,虚拟机会抛出 ArithmeticException 异常
  • Java 在处理浮点数运算时,不会抛出任何运行异常(Java 语言的异常)
  • 当一个操作产生溢出时,将使用有符号的无穷大表示,如果某个操作结果没有明确的数学定义的话,将会使用 NaN 表示
  • 所有使用 NaN 值作为操作数的算术操作,结果都返回 NaN
double a = 1;
double b = a / 0;  //&#x4E0D;&#x4F1A;&#x62A5;&#x9519;&#xFF0C;&#x7ED3;&#x679C;Infinity

double a = 0.0;
double b = a / 0.0; //&#x4E0D;&#x4F1A;&#x62A5;&#x9519;&#xFF0C;&#x7ED3;&#x679C;NaN

2.3 类型转换指令

类型转换指令可以将两种不同的数值类型进行互相转换,一般用于用户代码中的显示类型转换操作,隐式类型转换不同转换指令,虚拟机直接支持。

  • 显示类型转换指令
i2b  int &#x8F6C;&#x6362;byte
i2c  int &#x8F6C;&#x6362;char
i2s  int &#x8F6C;&#x6362;short
l2i  long &#x8F6C;&#x6362; int
f2i  float &#x8F6C;&#x6362; int
f2l  float &#x8F6C;&#x6362; long
d2i  double &#x8F6C;&#x6362; int
d2l  double &#x8F6C;&#x6362; long
d2f  double &#x8F6C;&#x6362; float
  • 转换规则
  • 如果浮点值是 NaN, 那转换结果就是 int 或者 long 类型的 0
  • 如果浮点值不是无穷大的话,浮点值使用 IEEE 754 的向零舍入模式去整,获取整数值 v,如果 v 在目标类型 T(int 或 long) 的标识表示范围之内,那转换结果就是 v。
  • 否则,将根据 v 的符号,转换为 T 所能表示的最大或最小正数。
double nan = 0.0 / 0.0;
int a = (int) nan;
System.out.println(a);  //0

float b = (float) nan;
System.out.println(b); //NaN

2.4 对象创建与访问指令

  • 创建类实例指令
new
  • 创建数组指令
newarray
anewarray
multianewarray
  • 访问类字段 和 实例字段
getfield
putfield

getstatic
putstatic
  • 加载数组元素到操作数栈
baload  //byte&#x6570;&#x7EC4;
caload  //char&#x6570;&#x7EC4;
saload  //short&#x6570;&#x7EC4;
iaload  //int&#x6570;&#x7EC4;
laload //long &#x6570;&#x7EC4;
faload //float &#x6570;&#x7EC4;
daload //double &#x6570;&#x7EC4;
aaload //&#x5BF9;&#x8C61;&#x6570;&#x7EC4;
  • 将操作数栈存储到数组元素中
bastore
castore
sastore
iastore
lastore
fastore
dastore
aastore
  • 获取数组长度
arraylength
  • 检查类实例类型的指令
instanceof
checkcast

2.5 操作数栈的管理指令

  • 出栈指令
pop
pop2 //&#x51FA;&#x6808;2&#x4E2A;&#x5143;&#x7D20;
  • 复制栈顶一个或者两个数值并复制或双份的复制值重新压入栈顶
dup
dup2
dup_x1
dup2_x1
dup_x2
dup2_x2
  • 将栈最顶端的两个数值互换
swap

2.6 控制转移指令

  • 条件分支
ifeq
iflt
ifle
ifne
ifge
ifnull
ifnonull
if_icmpeq  &#x6BD4;&#x8F83;&#x6808;&#x9876;&#x4E24;&#x4E2A;int&#x7C7B;&#x578B;&#x6570;&#x503C;&#x7684;&#x5927;&#x5C0F; ,&#x5F53;&#x524D;&#x8005;  &#x7B49;&#x4E8E;  &#x540E;&#x8005;&#x65F6;,&#x8DF3;&#x8F6C;
if_icmpne
if_icmplt
if_icmpgt
if_icmple
if_icmpge
if_acmpeq
if_acmpne
  • 复合条件分支
tableswitch   switch &#x6761;&#x4EF6;&#x8DF3;&#x8F6C; case&#x503C;&#x8FDE;&#x7EED;
lookupswitch  witch &#x6761;&#x4EF6;&#x8DF3;&#x8F6C; case&#x503C;&#x4E0D;&#x8FDE;&#x7EED;
  • 无条件分支
goto &#x65E0;&#x6761;&#x4EF6;&#x8DF3;&#x8F6C;
goto_w &#x65E0;&#x6761;&#x4EF6;&#x8DF3;&#x8F6C;  &#x5BBD;&#x7D22;&#x5F15;
jsr   SE6&#x4E4B;&#x524D; finally&#x5B57;&#x53E5;&#x4F7F;&#x7528; &#x8DF3;&#x8F6C;&#x5230;&#x6307;&#x5B9A;16&#x4F4D;&#x7684;offset,&#x5E76;&#x5C06;jsr&#x4E0B;&#x4E00;&#x6761;&#x6307;&#x4EE4;&#x5730;&#x5740;&#x538B;&#x5165;&#x6808;&#x9876;
jsr_w SE6&#x4E4B;&#x524D; &#x540C;&#x4E0A;  &#x5BBD;&#x7D22;&#x5F15;
ret  SE6&#x4E4B;&#x524D;&#x8FD4;&#x56DE;&#x7531;&#x6307;&#x5B9A;&#x7684;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x6240;&#x7ED9;&#x51FA;&#x7684;&#x6307;&#x4EE4;&#x5730;&#x5740;(&#x4E00;&#x822C;&#x914D;&#x5408;jsr  jsr_w&#x4F7F;&#x7528;)
w&#x540C;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x7684;&#x5BBD;&#x7D22;&#x5F15;&#x542B;&#x4E49;

2.7 方法调用和返回指令

  • 方法调用指令

invokevirtual: &#x8C03;&#x7528;&#x5BF9;&#x8C61;&#x5B9E;&#x4F8B;&#x65B9;&#x6CD5;
invokeinterface &#x8C03;&#x7528;&#x63A5;&#x53E3;&#x65B9;&#x6CD5;
invokespecial &#x8C03;&#x7528;&#x4E00;&#x4E9B;&#x9700;&#x8981;&#x7279;&#x9700;&#x5904;&#x7406;&#x7684;&#x5B9E;&#x4F8B;&#x65B9;&#x6CD5;&#xFF0C;&#x5305;&#x62EC;&#x5B9E;&#x4F8B;&#x521D;&#x59CB;&#x5316;&#x65B9;&#x6CD5;&#x3001;&#x79C1;&#x6709;&#x65B9;&#x6CD5;&#x3001;&#x7236;&#x7C7B;&#x65B9;&#x6CD5;
invokestatic  &#x8C03;&#x7528;&#x7C7B;&#x65B9;&#x6CD5;
invokedynamic  &#x5728;&#x8FD0;&#x884C;&#x65F6;&#x52A8;&#x6001;&#x89E3;&#x6790;&#x51FA;&#x8C03;&#x7528;&#x70B9;&#x9650;&#x5B9A;&#x7B26;&#x6240;&#x5F15;&#x7528;&#x7684;&#x65B9;&#x6CD5;&#xFF0C;&#x5E76;&#x6267;&#x884C;
  • 返回指令
ireturn
lreturn
freturn
dreturn
areturn
return &#x58F0;&#x660E;&#x4E3A;void &#x7684;&#x65B9;&#x6CD5;

2.8 异常处理指令

athrow  &#x663E;&#x793A;&#x629B;&#x51FA;&#x5F02;&#x5E38;

2.9 同步指令

Java 虚拟机可以支持方法级别的同步和方法内部一段指令序列的同步,这两种同步结构都使用管理(Monitor)来支持。

  • 方法级别的同步是由方法表结构中 ACC_SYNCHRONIZED 访问标识来处理
  • 方法内部一段指令序列的同步
monitorenter  &#x83B7;&#x53D6;&#x9501;&#xFF0C;&#x8FDB;&#x5165;&#x4EE3;&#x7801;&#x5757;
monitorexit   &#x91CA;&#x653E;&#x9501;&#xFF0C;&#x5FC5;&#x987B;&#x4E0E;monitorenter&#x6210;&#x5BF9;&#x51FA;&#x73B0;
  • 源码
public class SynchronizedInstruction {
    private Object lock=new Object();
    void onlyMe(Object lock){
        synchronized (lock){
            //doSomething
        }
    }
}
  • 反汇编
Compiled from "SynchronizedInstruction.java"
public class cn.hdj.jvm.bytecode.SynchronizedInstruction {
  private java.lang.Object lock;
  public cn.hdj.jvm.bytecode.SynchronizedInstruction();
  void onlyMe(java.lang.Object);
}
Classfile /home/hdj/IDEA/Java-Learning/src/main/java/cn/hdj/jvm/bytecode/SynchronizedInstruction.class
  Last modified 2021-3-20; size 488 bytes
  MD5 checksum 1f6db0fa955b6d719018d2ea50e1e910
  Compiled from "SynchronizedInstruction.java"
public class cn.hdj.jvm.bytecode.SynchronizedInstruction
  SourceFile: "SynchronizedInstruction.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #2.#19         //  java/lang/Object."<init>":()V
   #2 = Class              #20            //  java/lang/Object
   #3 = Fieldref           #4.#21         //  cn/hdj/jvm/bytecode/SynchronizedInstruction.lock:Ljava/lang/Object;
   #4 = Class              #22            //  cn/hdj/jvm/bytecode/SynchronizedInstruction
   #5 = Utf8               lock
   #6 = Utf8               Ljava/lang/Object;
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               onlyMe
  #12 = Utf8               (Ljava/lang/Object;)V
  #13 = Utf8               StackMapTable
  #14 = Class              #22            //  cn/hdj/jvm/bytecode/SynchronizedInstruction
  #15 = Class              #20            //  java/lang/Object
  #16 = Class              #23            //  java/lang/Throwable
  #17 = Utf8               SourceFile
  #18 = Utf8               SynchronizedInstruction.java
  #19 = NameAndType        #7:#8          //  "<init>":()V
  #20 = Utf8               java/lang/Object
  #21 = NameAndType        #5:#6          //  lock:Ljava/lang/Object;
  #22 = Utf8               cn/hdj/jvm/bytecode/SynchronizedInstruction
  #23 = Utf8               java/lang/Throwable
{
  private java.lang.Object lock;
    flags: ACC_PRIVATE

  public cn.hdj.jvm.bytecode.SynchronizedInstruction();
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: new           #2                  // class java/lang/Object
         8: dup
         9: invokespecial #1                  // Method java/lang/Object."<init>":()V
        12: putfield      #3                  // Field lock:Ljava/lang/Object;
        15: return
      LineNumberTable:
        line 8: 0
        line 9: 4

  void onlyMe(java.lang.Object);
    flags:
    Code:
      stack=2, locals=4, args_size=2
         0: aload_1       //&#x5C06;lock&#x5BF9;&#x8C61;&#x5165;&#x6808;
         1: dup           //&#x590D;&#x5236;&#x6808;&#x9876;&#x5143;&#x7D20;
         2: astore_2      //&#x5C06;&#x6808;&#x9876;&#x5143;&#x7D20;&#x5B58;&#x50A8;&#x5230;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;Slot2&#x4E2D;
         3: monitorenter  //&#x4EE5;lock&#x5BF9;&#x8C61;&#x4E3A;&#x9501;&#xFF0C;&#x5F00;&#x59CB;&#x540C;&#x6B65;
         4: aload_2       //&#x5C06;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;Slot2&#x4E2D;&#x5143;&#x7D20;&#x5165;&#x6808;
         5: monitorexit   //&#x9000;&#x51FA;&#x540C;&#x6B65;
         6: goto          14  //&#x7A0B;&#x5E8F;&#x6B63;&#x5E38;&#x7ED3;&#x675F;&#xFF0C;&#x8DF3;&#x8F6C;&#x5230;14&#x8FD4;&#x56DE;
         9: astore_3      //&#x4ECE;&#x8FD9;&#x6B65;&#x5F00;&#x59CB;&#x662F;&#x5F02;&#x5E38;&#x8DEF;&#x5F84;&#xFF0C;&#x5F00;&#x4E0B;&#x9762;&#x7684;Exception table
        10: aload_2       //&#x5C06;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;Slot2&#x4E2D;&#x5143;&#x7D20;&#x5165;&#x6808;
        11: monitorexit   //&#x9000;&#x51FA;&#x540C;&#x6B65;
        12: aload_3       //&#x5C06;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;Slot3&#x4E2D;&#x5143;&#x7D20;&#xFF08;&#x5F02;&#x5E38;&#x5BF9;&#x8C61;&#xFF09;&#x5165;&#x6808;
        13: athrow        //&#x628A;&#x5F02;&#x5E38;&#x5BF9;&#x8C61;&#x91CD;&#x65B0;&#x629B;&#x51FA;&#x4E2A;onlyMe&#x65B9;&#x6CD5;&#x8C03;&#x7528;&#x8005;
        14: return        //&#x65B9;&#x6CD5;&#x8FD4;&#x56DE;
      Exception table:
         from    to  target type
             4     6     9   any
             9    12     9   any
      LineNumberTable:
        line 11: 0
        line 13: 4
        line 14: 14
      StackMapTable: number_of_entries = 2
           frame_type = 255 /* full_frame */
          offset_delta = 9
          locals = [ class cn/hdj/jvm/bytecode/SynchronizedInstruction, class java/lang/Object, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
           frame_type = 250 /* chop */
          offset_delta = 4

}

</init></init></init></init></init>

三、例子解析

  • 代码
public class DemoDynamic {
    public static void foo() {
        int a = 1;
        int b = 2;
        int c = (a + b) * 5;
    }
}
  • javap 命令(也可以使用 IDEA 查看字节码工具:jclasslib)
javac -g -encoding utf-8 DemoDynamic.java
javap -verbose -c .\DemoDynamic.class >  .\DemoDynamic.javap
  • 字节文件
Classfile /D:/IDEA/Java-Learning/src/main/java/cn/hdj/jvm/bytecode/DemoDynamic.class
  Last modified 2020-10-17; size 419 bytes
  MD5 checksum 0242e2d86e94eb62d302f5a034336416
  Compiled from "DemoDynamic.java"
public class cn.hdj.jvm.bytecode.DemoDynamic
  minor version: 0  //&#x7248;&#x672C;&#x53F7;
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER  //&#x8BBF;&#x95EE;&#x6807;&#x8BC6;&#x7B26;
Constant pool:   //&#x5E38;&#x91CF;&#x6C60;
   #1 = Methodref          #3.#18         // java/lang/Object."<init>":()V
   #2 = Class              #19            // cn/hdj/jvm/bytecode/DemoDynamic
   #3 = Class              #20            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               Lcn/hdj/jvm/bytecode/DemoDynamic;
  #11 = Utf8               foo
  #12 = Utf8               a
  #13 = Utf8               I
  #14 = Utf8               b
  #15 = Utf8               c
  #16 = Utf8               SourceFile
  #17 = Utf8               DemoDynamic.java
  #18 = NameAndType        #4:#5          // "<init>":()V
  #19 = Utf8               cn/hdj/jvm/bytecode/DemoDynamic
  #20 = Utf8               java/lang/Object
{
  public cn.hdj.jvm.bytecode.DemoDynamic(); //&#x9ED8;&#x8BA4;&#x7684;&#x6784;&#x9020;&#x65B9;&#x6CD5;
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
    //&#x6808;&#x5BB9;&#x91CF;1 , &#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;&#x5BB9;&#x91CF;1, &#x53C2;&#x6570;&#x4E2A;&#x6570;1&#xFF08;&#x56E0;&#x4E3A;&#x6BCF;&#x4E2A;&#x5B9E;&#x4F8B;&#x65B9;&#x6CD5;&#x90FD;&#x4F1A;&#x6709;&#x4E00;&#x4E2A;&#x9690;&#x85CF;&#x53C2;&#x6570;this&#xFF09;
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcn/hdj/jvm/bytecode/DemoDynamic;

  public static void foo();  //foo()    &#x65B9;&#x6CD5;
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC  //&#x6807;&#x8BC6;&#x7B26;&#xFF0C;public static
    Code:  //&#x65B9;&#x6CD5;&#x8868;&#x4E2D;Code &#x5C5E;&#x6027;
      stack=2, locals=3, args_size=0  //&#x6808;&#x5BB9;&#x91CF;2 , &#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;&#x5BB9;&#x91CF;3, &#x53C2;&#x6570;&#x4E2A;&#x6570;0
         0: iconst_1  //  &#x5C06;&#x5E38;&#x91CF;&#x503C;1&#x5165;&#x6808;->  &#x6808;1=1
         1: istore_0  //  &#x5C06;&#x6808;&#x9876;&#x5143;&#x7D20;&#x5B58;&#x50A8;&#x5230;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;Slot1&#x4F4D;&#x7F6E;  -> &#x5C40;&#x90E8;0=1
         2: iconst_2  //  &#x5C06;&#x5E38;&#x91CF;&#x503C;2&#x5165;&#x6808; ->  &#x6808;1=2
         3: istore_1  //  &#x5C06;&#x6808;&#x9876;&#x5143;&#x7D20;&#x5B58;&#x50A8;&#x5230;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;Slot2&#x4F4D;&#x7F6E;    ->  &#x5C40;&#x90E8;1=2
         4: iload_0   //  &#x5C06;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;Slot1&#x4E2D;&#x5143;&#x7D20;&#x5165;&#x6808;
         5: iload_1     // &#x5C06;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;Slot2&#x4E2D;&#x5143;&#x7D20;&#x5165;&#x6808;
         6: iadd        // &#x6267;&#x884C;&#x76F8;&#x52A0;&#x64CD;&#x4F5C;&#xFF0C; 1+2 = 3, &#x5165;&#x6808;
         7: iconst_5    // &#x5C06;&#x5E38;&#x91CF;&#x503C;5&#x5165;&#x6808;
         8: imul        // &#x6267;&#x884C;&#x76F8;&#x4E58;&#x64CD;&#x4F5C;&#xFF0C;3*5=15,&#x5165;&#x6808;
         9: istore_2    // &#x5C06;&#x6808;&#x9876;&#x5143;&#x7D20;&#x5B58;&#x50A8;&#x5230;&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;Slot2&#x4F4D;&#x7F6E;-> &#x5C40;&#x90E8;2=15
        10: return    //&#x8FD4;&#x56DE;
      LineNumberTable: //&#x884C;&#x6570;&#x8868;
        line 9: 0
        line 10: 2
        line 11: 4
        line 12: 10
      LocalVariableTable:  //&#x5C40;&#x90E8;&#x53D8;&#x91CF;&#x8868;
        Start  Length  Slot  Name   Signature
            2       9     0     a   I
            4       7     1     b   I
           10       1     2     c   I
}
SourceFile: "DemoDynamic.java"

</init></init></init></init>

JVM学习笔记之class文件结构【七】

四、字节码增强

具体详情看 字节码增强技术探索,这里只简单列出相关工具及使用场景。

JVM学习笔记之class文件结构【七】

4.1 ASM

对于需要手动操纵字节码的需求,可以使用 ASM,它可以直接生产 .class 字节码文件,也可以在类被加载入 JVM 之前动态修改类行为

JVM学习笔记之class文件结构【七】
  • ASM 工具 辅助工具
  • IDEA 插件ASM ByteCode Outline,用于查看类中的代码对应的 ASM 写法

4.2 Javassist

利用 Javassist 实现字节码增强时,可以无须关注字节码刻板的结构,其优点就在于编程简单。直接使用 java 编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构或者动态生成类。

4.3 Instrument

instrument 是 JVM 提供的一个可以修改已加载类的类库,专门为 Java 语言编写的插桩服务提供支持。它需要依赖 JVMTI 的 Attach API 机制实现。注意:ASM 和 Javassist 操作字节码库只能在类加载前对类进行强化。

4.5 字节码增强技术使用场景

  • AOP 面向切面编程
  • 热部署:不部署服务而对线上服务做修改,可以做打点、增加日志等操作。
  • Mock:测试时候对某些服务做 Mock。
  • 性能诊断工具:比如 bTrace 就是利用 Instrument,实现无侵入地跟踪一个正在运行的 JVM,监控到类和方法级别的状态信息。

参考

Original: https://www.cnblogs.com/JianJianHuang/p/14562043.html
Author: JiaJianHuang
Title: JVM学习笔记之class文件结构【七】

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/573078/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Elasticsearch 入门实战(7)–Data Stream

    数据量 (Data Stream) 是在 Elasticsearch 7.9 版推出的一项功能,它可以很方便的处理时间序列数据。 1、简介 1.1、什么是 Time Series …

    Java 2023年6月16日
    076
  • Spring —Spring专题(一)

    Spring简介 1.1 什么是Spring spring是分层的java SE/EE应用full-stack(全栈)轻量级开源框架,以 IOC(Inverse Of Contro…

    Java 2023年6月5日
    074
  • SpringBoot 开发案例之参数传递的正确姿势

    前言 开发这么多年,肯定还有不少小伙伴搞不清各种类型的参数是如何传递的,很多同学都是拿来即用,复制粘贴一把撸,遇到问题还是一脸懵逼。 姿势 学习参数传递的正确姿势,先说怎么做,再说…

    Java 2023年5月30日
    076
  • java代码生成封面

    public class XWPFTest {public static void main(String[] args) throws IOException {//创建一个do…

    Java 2023年5月29日
    057
  • 钉钉的sonar集成通知

    钉钉(dingding)的sonar(代码质量管理工具的)集成通知,非常简单的一个小开源工具。 代码地址: https://gitee.com/chejiangyi/dingdin…

    Java 2023年6月8日
    066
  • Spring自定义解析的集中方式

    springMVC 、springboot中返回前端JSON 时候,经常需要不同的格式 实现方式有几种 一 、自己实现JSON序列化器 二、 自定义注释 Original: htt…

    Java 2023年5月30日
    069
  • 打工四年总结的数据库知识点

    国庆在家无聊,我随手翻了一下家里数据库相关的书籍,这一翻我就看上瘾了,因为大学比较熟悉的一些数据库范式我居然都忘了,怀揣着好奇心我就看了一个小国庆。 看的过程中我也做了一些小笔记,…

    Java 2023年6月9日
    0140
  • 【每天学一点-06】在Vue中使用Vant-Picker选择器,并且给选择器添加一个类似Antd-Select-showSearch的搜索功能

    一、需求梳理 1、Vant-Picker 文档 2、Antd-Select 文档 3、需要完成的需求 4、因为在H5项目中出现了类似需求,也就是在Picker-title的位置加一…

    Java 2023年6月5日
    072
  • 学Java,Java书籍的最佳阅读顺序

    疫情以来,好久没出差了,今天出差去趟上海,早上 4 点多就起床了,到机场天都没亮。到登机口离起飞还一小时,趁着等飞机的时间,抓紧码字,把这篇文章收个尾。 今天和大家说说学 Java…

    Java 2023年6月7日
    049
  • CAS 单点登录【2】自定义用户验证

    方案1:CAS默认的JDBC扩展方案: CAS自带了两种简单的通过JDBC方式验证用户的处理器。 这两个处理类位于cas-server-support-jdbc这个扩展工程下。 第…

    Java 2023年5月29日
    066
  • 基于Python来识别处理照片里的条形码

    最近一直在玩数独,突发奇想实现图像识别求解数独,输入到输出平均需要0.5s。 整体思路大概就是识别出图中数字生成list,然后求解。 输入输出demo 数独采用的是微软自带的Mic…

    Java 2023年6月7日
    070
  • 简单易懂讲文件

    注意事项 如果运行代码的时候找不到文件,但是文件的的确确又存在,检查下 idea 的工作路径 路径 Path Path 对象是将一个路径封装成一个对象,然后通过这个对象来执行路径的…

    Java 2023年6月8日
    096
  • JUC学习

    如何正确停止线程? 停止线程应该是一种通知协作的方式,比如interrupt,但是它仅仅是通知线程,线程拥有完全的自主权,根据自身业务来判断什么时候停止,因为如果选择立即停止就可能…

    Java 2023年6月8日
    064
  • mybatis-plus代码生成器2.0

    要在springboot项目下使用! 相关依赖 org.springframework.boot spring-boot-starter org.springframework.b…

    Java 2023年6月9日
    091
  • Spring常见问题

    Spring常见问题 问渠那得清如许?为有源头活水来。 Spring 是个 java 企业级应用的开源开发框架。Spring 主要用来开发 Java 应用,但是有些扩展是针对构建 …

    Java 2023年6月5日
    057
  • C C++结构体四种方式

    第一种语法表示 struct &#x7ED3;&#x6784;&#x4F53;&#x540D;&#x79F0; {&#xA0; &a…

    Java 2023年6月16日
    061
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球