美文网首页
JVM读书笔记-类文件结构

JVM读书笔记-类文件结构

作者: 空而小sao | 来源:发表于2019-08-25 21:25 被阅读0次

以前只知道,java文件运行javac命令后 将会生成一个.class文件,继而运行java命令运行该class文件即可。也曾打开过class文件,里面一堆16进制数字,肉眼一瞟,什么**玩意?页面停留3秒,close~

那就来探索一下,这些个16进制数据究竟是什么鬼怪;

类的数据性结构

第一步,要认知到,所有的这些的class,都是严格按照如下一张表属性,前后顺序排列。
类的文件结构主要体现为这张表,从这张表开始认识:


1566711447(1).jpg

U: U1,U2,U4,U8,类型分别表示对应多少字节,这个用于在那些一堆16进制数字中,套取对应的值。例如:第一个属性 magic为U4,则该属性对应的值就是class文件那一堆数字中,最前面4个字节表示的16进制值。以此类推,后边的属性值,顺序读取。

_info: 后面有加info类型,则表示该属性是由多个组数据集合而成,例如:第一个info---constant_pool,则需要与U2---constant_pool_count 结合使用,字面意思就能理解,表示有多少个count 的constant; 每个constant按照该属性规则去划分多少字节。

这样就能把这个class的所有16进制数字,分别按照这个字节规则去从前往后划分,机器自然就能读出当前属性是什么,代表什么含义;机器是笨的,只会按照规则行事,创造这些规则的才是智者

属性

  1. magic与version

    每个Class文件的头4个字节称为魔数(magic),它的唯一作用是判断该文件是否为一个能被虚拟机接受的Class文件。它的值固定为0xCAFEBABE。紧接着d的4个字节是Class文件的次版本号和主版本号,高版本的JDK能向下兼容低版本的Class文件,但不能运行更高版本的Class文件。(copy 来的介绍)

  2. constant_pool(常量池)

    前面的属性位数读取完后,就是该属性的开始,(占用空间最大的属性);

    主要分为两大类常量:

    • 字面量 :和java层类似,例如Int,Float,double等等;

    • 符号引用

      1. 类和接口的全限定名:也就是Ljava/lang/String;

      2. 字段的名称和表述(private,public等)

      3. 方法的名称和表述(private,public等)

    符号引用和直接引用的区别与关联??难以理解,待解答
    引用指的是需要通过其他的常量,显示当前对应的属性,存放的其他常量的地址

常量池中的每一项常量都是一个表(网上盗图--对应表--6-3)

1566725611(1).jpg

每个常量都有自己的数据结构,字面量里的数据结构放着就是标志和值 (tag -- bytes),符号 引用里的则是存放另一个常量的地址,(tag -- index --index)有些符号引用数据结构中有两个index引用,例如:Methodref_info等,具体可参照书中 表6-6。

注: 由于所有的class名或者字段名,用到的描述都是第一个属性Utf-8_info,而它的结构(tag--length--bytes),最大占用字节为U2,所以方法名的长度不能超过64KB。不过哪有方法名这么长的???

通过这个表和查找方法就可以将class中所有常量,都能一一找出来,既然这是机器所做的操作,人为一个个去找实在太傻,自然也有机器的方法:。 Java类分解器 javap -verbose 能帮你解读当前的class文件,再也不用去看那些16进制的鬼东西

写了一个demo:

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

编译后进行 javap -verbose

C:\Users\Administrator\Desktop\aaaa>javap -verbose TestClass.class
Classfile /C:/Users/Administrator/Desktop/aaaa/TestClass.class
 Last modified 2019-8-25; size 296 bytes
 MD5 checksum d59bab4f31f6cbf544dcfeb5a3471d2c
 Compiled from "TestClass.java"
public class com.porterking.clazz.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         // com/porterking/clazz/TestClass.m:I
 #3 = Class              #17            // com/porterking/clazz/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               com/porterking/clazz/TestClass
 #18 = Utf8               java/lang/Object
{
 public com.porterking.clazz.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=2, locals=1, args_size=1
 0: aload_0
 1: getfield      #2                  // Field m:I
 4: iconst_1
 5: iadd
 6: ireturn
 LineNumberTable:
 line 6: 0
}
SourceFile: "TestClass.java"

用这个显示去读class文件,不要太友好。个人认为,校对每个属性的表结构,然后一一去解读每位16进制的所代表的意思,能帮你充分理解上述代码如何生成,但具体去分析class文件,只需要读懂当前解析过之后的上述代码即可;

java字节代码解析主要分为三块,常量池,字段,方法

1. 常量池: 这里罗列了18项常量,每一项结合对应属性表的结构,包括引用属性 通过“#“,引用到对应的常量,一幕了然。还看到,常量里的utf8 属性展示的一些名称,<init> ()V等 这些都是其他类属性所需要用到的常量,所以这个常量池是参与其他项目关联最多的,所以也是占用class文件控件最大的项目之一

感悟:java在编译时,根据当前代码,就帮你先声明好一块区域,也就是常量池,里面放的都是静态的一些值,例如方法名,字段名等等这些例如我们肉眼看到的字符,都给你编译成了那些16进制数字。而放入常量池中,在运行编译时,就可以通过地址获取到值。结合String str = "abc"; String str = new String("abc") 两者实现在字节码上的实现不同点, 能有效的理解常量池的作用。运行时时常量,有待深入考察。

2. 字段: 测试代码中,并没有将 字段m设为public属性,因此没有这里申明,书上未做解释,个人认为,只有public或者protected 属性,需要被外部引用的,才会在字节码上有申明,不然也就只是在常量中存在。根据属性表的结构:1. descriptor--类型 2.flags--访问标志 3.ConstantValue--属性表集合(只有当前字段被final static修饰时才会有)

descriptor--类型,这个标识描述符的作用则是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序等)和返回值的。根据描述符规则,详细的描述符标示字的含义如下表所示,这与JNI中的全限定名如同一辙:

1566735098(1).jpg

3. 方法: 数据结构与字段一致,就是attribute属性表所带不同,每个方法名一般都带有Code属性(除接口和者抽象方法)

Code数据结构:该属性为class代码最重要的属性,用于描述方法,阅读懂它就能知道它所描述的方法是什么

  • stack:操作数栈,方法套嵌的层数。

  • locals :局部变量所需要的空间单位Slot,基本类型一个变量1Slot,double、long为2Slot

  • args_size:参数个数,对于实例方法每个方法至少带有一个this的参数,static方法args_size从0开始计数

  • 操作指令:目前Java虚拟机规范定义了其中200条指令,具体每一条指令是什么,《深入理解Javan虚拟机》中并没有一一阐述,看来是需要某搜索引擎查询; 上述指令中,aload_0 :含义为将第0个Slot中reference类型的变量推送到操作数栈顶;invokespecial :以栈顶reference类型数据作为接受者,调用实例构造方法,这个操作需要说明是什么方法,则指向了常量中的 #1 Methodref方法引用 return:返回指令

Code中的操作指令,也就给机器规定好一条一条的规则,逐条操作指令。这一块就很机器化了。

方法中还有其他的基本属性,

Exception属性:列举出方法中可能抛出的受查异常

LineNumberTable属性:描述Java源码行号与字节码行号之间的对应关系。

LocalVariableTable属性:描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的对应关系。

SourceFile属性:记录生成这个Class文件的源码文件名称。

属性表还有很多属性,就不全部展示

总结

学完这一章后,对于这个class文件,亲切了许多,不再是一头雾水。也能看的懂 javap 编译后的文件。 算有所收获吧,也能为后面的类加载机制垫底一些基础。

相关文章

  • JVM类加载学习二-类加载机制学习

    JVM类加载机制 @(Java)[JVM|类文件结构] [TOC] 基本介绍 JVM的类加载机制:JVM把描述类的...

  • JVM类文件结构

    JVM的类文件通俗讲就是编译后的class文件。由于文件名是可以任意更改的,所以我们不能说以.class为后缀的文...

  • 【JVM】类文件结构

    Class文件是一组以8位字节为基础单位的二进制流,各个数据项目按照顺序紧凑地排列在Class文件中,中间没有任何...

  • JVM类文件结构

    Class文件 Class 文件是一组以字节为基础单位的进制流 两种数据类型 无符号数,属于基本数据类型, u1,...

  • JVM读书笔记-类文件结构

    以前只知道,java文件运行javac命令后 将会生成一个.class文件,继而运行java命令运行该class文...

  • 深入JVM内核8 类连接和初始化

    1.类连接 1.1 类连接主要验证的内容 类文件结构检查:按照JVM规范规定的类文件结构进行 元数据验证:对字节码...

  • JVM执行系统

    2.10 JVM执行系统 2.10.1 类文件结构 JVM是不和Java语言强绑定的,它只与Class文件这种特定...

  • 图解jvm--(三)类加载与字节码技术

    类加载与字节码技术 1.类文件结构 根据 JVM 规范,类文件结构如下 2.字节码指令 指令作用iconst_1i...

  • JVM相关 : 3. 类加载和字节码

    1. 类文件结构 根据jvm规范,类文件结构如下: 1.1 魔数 0~3 字节,表示它是否是 class 类型的文...

  • Java 底层机制

    JVM体系结构 JVM是一种解释执行class文件的规范技术。 JVM体系结构 我翻译的中文图: 中文图 类装载器...

网友评论

      本文标题:JVM读书笔记-类文件结构

      本文链接:https://www.haomeiwen.com/subject/wlikectx.html