这篇博客开始,我规划带各人去解读一下JVM平台下的字节码文件(熟悉而又生疏的感受)。众所周知,Class文件包括了我们界说的类或接口的信息。然后字节码又会被JVM加载到内存中,供JVM利用。那么,类信息到了字节码文件里,它们如何暗示的,以及在字节码里是怎么漫衍的呢?带着这些问题,让我们去深入相识字节码文件吧。
Class文件的布局
Class文件是一组以8位字节为基本单元的二进制流,各个数据项目严格凭据顺序紧凑地分列在Class文件之中,中间没有添加任何脱离符,这使得整个Class文件中存储的内容险些全部是措施运行的须要数据,没有旷地存在。当碰着需要占用8位字节以上空间地数据项时,劳务派遣管理系统,则会凭据高位在前的方法支解成若干个8位字节举办存储。
每一个 Class 文件对应于一个如下所示的 ClassFile 布局体。
ClassFile { u4 magic; 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]; }
这种数据布局,雷同C语言布局体。这个布局体中只有两种数据范例:无标记数和表,后头的理会都要以这两种数据范例为基本,所以这里要先先容这两个观念。
无标记数属于根基的数据范例,以u1,u2,u4,u8来别离代表1个字节,2个字节,4个字节和8个字节的无标记数,无标记数可以用来描写数字、索引引用、数量值可能凭据UTF-8编码组成字符串值。
表是由多个无标记数可能其他表作为数据项组成的复合数据范例,所有表都习惯性地以“_info”末了。表用于描写有条理干系的复合布局的数据,整个Class文件本质就是一张表。
下面是我的案例代码,本章将以此代码生成的字节码文件作为例子来阐明。
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; } }
对应生成的字节码文件名目如下:(数据内容较多,只是截了部门)
昆山软件开拓 个符号位" class="aligncenter size-full wp-image-30465" title="20181014145935732" src="/uploads/allimg/c181119/15425G3162C40-1T37.png" />
上面的数字是以16进制暗示的。我们可以凭据之前的布局一项项去解读它。
Class文件理会
魔数,u4范例的数据,占4个字节。魔数的独一浸染是确定这个文件是否为一个能被虚拟机所接管的 Class 文件。魔数值牢靠为 0xCAFEBABE(咖啡宝物),不会改变。
紧接着魔数之后的4个字节为Java版本信息:第5和第6个字节是次版本号(minor_version),第7和第8个字节是主版本号(major_version)。
就看当前这个字节码,次版本号是0×0000=0,主版本号是0×0034=52。我当地呆板用的是JDK1.8,所以可生成的Class文件主版本号最大值为52.0。
下面给出了Java各个主版本号,以供参考。
昆山软件开拓 个符号位" class="aligncenter size-full wp-image-30464" title="Snipaste_2018-11-18_20-12-02" src="/uploads/allimg/c181119/15425G316323F-253Q.png" />
常量池计数器,u2范例的数据。它是常量池的进口,暗示紧随着它后头的常量池的元素个数。算一下,0x002F=47,即常量池里的元素有47个。这里我用jdk的内置东西javap,反编译一下,可以输出常量池的信息以及元素个数。执行呼吁:javap -verbose。输出功效如下:
Constant pool: #1 = Methodref #10.#34 // java/lang/Object."<init>":()V #2 = String #35 // Welcome #3 = Fieldref #5.#36 // com/shengsiyuan/jvm/bytecode/MyTest2.str:Ljava/lang/String; #4 = Fieldref #5.#37 // com/shengsiyuan/jvm/bytecode/MyTest2.x:I #5 = Class #38 // com/shengsiyuan/jvm/bytecode/MyTest2 #6 = Methodref #5.#34 // com/shengsiyuan/jvm/bytecode/MyTest2."<init>":()V #7 = Methodref #5.#39 // com/shengsiyuan/jvm/bytecode/MyTest2.setX:(I)V #8 = Methodref #40.#41 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #9 = Fieldref #5.#42 // com/shengsiyuan/jvm/bytecode/MyTest2.in:Ljava/lang/Integer; #10 = Class #43 // java/lang/Object #11 = Utf8 str #12 = Utf8 Ljava/lang/String; #13 = Utf8 x #14 = Utf8 I #15 = Utf8 in #16 = Utf8 Ljava/lang/Integer; #17 = Utf8 <init> #18 = Utf8 ()V #19 = Utf8 Code #20 = Utf8 LineNumberTable #21 = Utf8 LocalVariableTable #22 = Utf8 this #23 = Utf8 Lcom/shengsiyuan/jvm/bytecode/MyTest2; #24 = Utf8 main #25 = Utf8 ([Ljava/lang/String;)V #26 = Utf8 args #27 = Utf8 [Ljava/lang/String; #28 = Utf8 myTest2 #29 = Utf8 setX #30 = Utf8 (I)V #31 = Utf8 <clinit> #32 = Utf8 SourceFile #33 = Utf8 MyTest2.java #34 = NameAndType #17:#18 // "<init>":()V #35 = Utf8 Welcome #36 = NameAndType #11:#12 // str:Ljava/lang/String; #37 = NameAndType #13:#14 // x:I #38 = Utf8 com/shengsiyuan/jvm/bytecode/MyTest2 #39 = NameAndType #29:#30 // setX:(I)V #40 = Class #44 // java/lang/Integer #41 = NameAndType #45:#46 // valueOf:(I)Ljava/lang/Integer; #42 = NameAndType #15:#16 // in:Ljava/lang/Integer; #43 = Utf8 java/lang/Object #44 = Utf8 java/lang/Integer #45 = Utf8 valueOf #46 = Utf8 (I)Ljava/lang/Integer;