jvm_类文件结构

计算机是只能识别0和1的,Java的设计者创新性的提出了跨平台的概念,
把Java编译成字节码文件,运行在 jvm虚拟机中,从而可以达到跨平台的概念。
Java只不过是jvm支持的众多语言中的最广泛的一种 ,Java和jvm虚拟机并不是绑定在一起的。
实际上,不论哪种语言,只要他的最终产物编译成字节码,都是可以运行在jvm之上的,例如 groovy,scala,jython 等


字节码查看工具: jclasslib 

https://github.com/ingokegel/jclasslib

1. 反编译

命令行工具: 
    1. javap -verbose
        分析 魔数,版本号,常量池,类信息,构造方法,成员方法,变量
    2. javap -c xxx
    3. 十六进制查看 
        mac :hex_fiend 。 win :winhex

1. 魔数

字节数: U4
Java采用魔数来识别字节码文件而不是后缀,从而规避安全性风险。
1. class文件的头四个字节是魔数  CA FE BA BE

2. 版本号

字节数: U4
挨着四个字节代表版本号
其中前2字节代表次版本号 minor version,
后2字节代表主版本号 major version

3. 常量池 constant pool

常量池个数的字节数: U2 + N
1. 主版本号之后的是常量池入口,一个Java类中定义的很多信息都是由常量池来维护的,
我们可以将常量池看做class文件的资源库,比如 方法变量信息,
2. 常量池主要存储2中数据, 字面量和符号引用 
    字面量: 文本字符串,比如final常量值
    符号引用:类和接口的全局限定名,字段以及方法的名称和描述符
3. 常量池的总体结构:Java类所对应的常量池主要由常量池数量和常量池数组构成

3. 常量池数量和常量池数组

1. 常量池数量紧跟主版本后,占用2个字节。 
2. 常量池数组紧跟常量池数量,与一般数组不同的是,它的元素类型,结构都是不相同		的。
    每一种元素的第一个数据都是一个u1类型,该字节是一个标志位,占1个字节,
    jvm在解析常量池时,会根据这个u1类型获取元素具体类型,
    常量池数组中元素的个数等于 常量池数-1(其中0暂时不使用),目的是满足某些
    常量池索引值的数据在特定情况下表达 不引用任何常量池的含义。
    根本原因是所以0也是一个常量(保留常量),不过它不位于长量表中,对应null值,
    常量池索引从1开始

常量池对照表

字节码整体结构

在jvm规范中,每个变量/字段都有描述信息,描述信息的主要作用就是描述
字段的数据类型,方法的参数列表(数量,类型,顺序)与返回值
基本数据类型和代表返回值void的类型都用一个大写字符标识,对象类型使用L加上
对象的全限定名称来标识,为了压缩字节码体积
基本数据类型都是用一个大写字符标识: 
B - byte。C - chart。D - double。 I - int 。J - long。F - float。 
z - boolean。V - void。S - short。 
L - 对象类型,如 Ljava/lang/String
对于数组: 使用 前置的方括号[ 。如 int ,记录为 [I
二维数组: 使用前置的[[ 记录,比如 [[I

方法的描述符
先参数列表后返回值类型,参数列表按照严格顺序放在一组()内
比如: 方法 String getName(int id,string name)
(I,Ljava/lang/String;)Ljava/lang/String;

4. accessFlag 访问标志

字节数: U2
示例 : 0x0021 是 acce_public 和 acc_super的并集

5. this class Name

字节数: u2

6. super class name

字节数: u2

7. interfaces

字节数: u2 + N

8. fields

字节数: u2 + N
字段表 : 用于描述类和接口中声明的变量。包括类变量和实例变量,但是不包括方法内局部变量
变量由以下几部分信息组成fieldInfo 
    1. 访问修饰符 U2
    2. 名字索引 U2
    3. 描述符索引 U2 (类型)
    4. attribute count
    5. attribute info

9. methods

字节数: U2 + N
方法数量: U2 默认会有一个无参数的构造方法,所以方法+1
方法表 :
    1. 访问修饰符 U2
    2. 名字索引 U2
    3. 描述符索引 U2 (类型)
    4. attribute count
    5. attribute info
方法属性结构表: 
    1. 名字索引 U2
    2. 属性长度 U4
    3. 具体信息 U1
code 结构表:
    stack 方法执行的最大栈的深度
    local 方法运行期间创建的局部变量的数目,包括入参的局部变量
    args_size 至少为1,因为this是隐式传递的
    助记符

10. attributes

字节数: U2 + N