jvm--1.class文件结构

1.字节码
(1)bytecode是构成平台无关性的基石

(2)当jvm发展到1.7-1.8的时候,jvm设计者通过,JSR-292,基本可以让其他语言运行在jvm上面。
如,Clojure , Groovy , JRuby , Jython , Scala

(3)jvm不和包括java在内的任何语言绑定, 它只和Class文件这种二进制文件格式关联。
Class文件,包含了jvm 指令集 和 符号表 ,以及其他若干信息。

(4)虚拟机不关心class文件的来源是何种语言。

java程序(*.java) --> java编译器

JRuby程序(*.rn) --> jruby编译器
---->字节码(*.class) -->java虚拟机
Groovy程序(*.groovy) --> groovy编译器

其他语言 --> 其他编译器

2.Class文件结构
概念:
(1)class文件,并非一定是一个磁盘文件,类或者接口也可以通过类加载器直接生成。
(2)class文件,是一组以8位字节为基础单位的二进制流。没有任何分割符号。
(3)class文件格式采用一种类似于C语言的结构体的伪结构来存储数据。
这种为机构只有两种数据类型:无符号数 和 表。

a.无符号数,属于最基本的数据类型,以u1 , u2 , u4 , u8 分别代表 1个字节 , 2个字节 , 4个字节 , 8个字节
可以用来描述,数字、索引引用、数量值、字符串

b.表,由无符号数,或其他表,作为数据项构成的,复合型数据类型。
所有的表习惯性的以 ”_info“ 结尾。
用于描述有层次关系的复合结构的数据。
整个class文件本质上就是一张表。
结构:
(4)魔数
a.每个Class文件的头四个字节称为魔数(Magic Number) ,它的唯一作用是用来确定这个文件是否能被jvm所接受的Class文件。
值为:0xCAFEBABE

(5)版本号
a.紧跟着魔数的四个字节,存储的是class文件的版本号:
第5和第6个字节是次版本号,第7和第8个字节是主版本号。
b.java版本号是从45开始的,JDK1.1之后的每个大版本发布,主版本号向上加1
JDK1.1支持: 45.0 ~45.65536
JDK1.2支持: 46.0 ~46.65536
JDK1.3支持: 47.0 ~47.65536
JDK1.4支持: 48.0 ~48.65536
JDK1.5支持: 49.0 ~49.65536
JDK1.6支持: 50.0 ~50.65536
JDK1.7支持: 51.0 ~51.65536
JDK1.8支持: 52.0 ~52.65536
c.高版本的JDK能向下兼容以前版本的Class文件,但低版本不能运行高版本的Class文件。即使文件格式没有发生变化,虚拟机也必须
拒绝执行,超过其版本号的Class文件

d.jdk的版本号和jvm的版本号对应。

(6) 常量池(每个Class文件都有)
a.紧接着主次版本号的是常量池入口,常量池可以理解为Class文件中的资源仓库,第一个出现的表类型数据结构。
由于常量池常量的数量是不固定的,所以,在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值(constant_pool_count)。
这个计数值是从1而不是从0开始的。因为某些指向常量池的索引值在特定情况下需要表达,不引用任何一个常量池。

b.存放:
字符串,声明为final的常量值。
类和接口的全限定名(com.lvyf.Test)
字段的名称和描述
方法的名称和描述,不包括方法返回值

c.*.java文件在进行,javac编译的时候,并不像C有连接这一步,而是在jvm加载Class文件的时候进行动态连接。也就是说,Class文件不会
保存各个方法,字段,的内存布局。因此,这些字段、方法不经过运行期转换的话无法得到内存地址,也就无法被虚拟机使用。
(就是 javac在编译的时候,把方法字段需要的内存信息,放到常量池,而不给方法和字段分配内存地址)当JVM运行Class文件时,
需要从常量池,获取对应的符号引用,再在类创建或者运行时解析、翻译到具体的内存地址。

d.java常量池有个计数器(因为常量池是不固定的,需要有计数器保存常量池长度),计数器数据类型是u2(2个字节2的16次方,65536),所以
java中如果定义超过64KB的英文变量名或者方法名,将会无法编译。

(7)访问标志
常量池结束之后,是访问标志(access_flag),识别类或者接口的访问信息
a.Class是类还是接口
b.是否为public类型
c.是类的话,是否为abstract类型
d.是否为final类型

(8)字段表集合field_info,用于描述接口或类中的变量
包括类变量,和实例级变量,不包括局部变量
a.字段的作用域(public , private , protected)
b.是实例变量还是类变量(static修饰)
c.可变性(final修饰)
d.并发可见性(volatile修饰,是否强制从主内存读写)
e.是否可被序列化(transient修饰)
f.字段类型(8中基本类型,对象,数组)
h.字段名称。
各个修饰符都是boolean值

(9)方法表集合,
a. 访问标志
名称索引
等。。。
b.code,方法里面的代码,经过编译成字节码后,存放在方法表集合中一个名为code的属性里面,接口没有code属性
code最大值也是65536
c.在java语言中,要重载一个方法,除了要与原方法有相同的名字,还要求必须与原方法有一个不同的特征签名,
特征签名,就是方法信息在常量池中的描述,这个描述,不包括方法的返回值,所以,不能根据返回值的不同,来重载方法。
要根据参数类型和参数个数,来重载方法。

(10)max_stack
代表了操作数栈深度的最大值。在方法执行的任意时刻,操作数栈都不会超过这个深度。

(11)max_locals
代表了局部变量所需的存储空间。单位是slot ,slot是虚拟机为局部变量分配内存的最小单位。
对于byte,char,float,int,short,boolean等长度不超过32位的数据类型,需要1个slot,
double,long需要两个slot

原文地址:https://www.cnblogs.com/fubaizhaizhuren/p/5938472.html