JVM之类文件结构

来源:互联网 发布:防网络诈骗图片 编辑:程序博客网 时间:2024/06/08 18:47

代码编译的结果从本地机器码转变为字节码

1、概述

传统的将程序编写后需要进行编译成本地机器码,计算机只识别0和1,为了跨平台、跨语言,一种特殊的存储格式字节码出现了,编译的后的结果不再是机器码而是字节码。它可以被载入虚拟机当中。

2、Java语言规范、Java虚拟机规范

这里写图片描述

Java虚拟机是运行着操作系统之上的一个应用程序,在虚拟机之上不仅仅可以运行Java程序,还可以运行其它的程序。

3、Class类文件结构

解析Class文件的数据结构
以Java虚拟机规范为主线(指令、属性)

tips:任何一个Class文件都对应着唯一一个类或接口定义的信息,但是反过来说,类或接口并不一定都得定义在文件里(比如类或接口也可以通过类加载器直接生成。)

Class文件是一组以8位字节为基础单位的二进制流,Class文件格式采用一种类似于C语言结构体的伪结构来存储数据, 这种伪结构中只有两种数据类型: 无符号数和表

无符号数:

无符号数属于基本的数据类型, 以u1、 u2、 u4、 u8来分别代表1个字节、 2个字节、 4个字节和8个字节的无符号
数, 无符号数可以用来描述数字、 索引引用、 数量值或者按照UTF-8编码构成字符串值。

表:

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

这里写图片描述

Class文件格式解析:
java代码:

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

class字节码16进制表示:

这里写图片描述

—魔数:0xCAFEBABE

每个Class文件的头4个字节称为魔数( Magic Number) , 它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件

—Class文件版本:0x0000

第5和第6个字节是次版本号( Minor Version) , 第7和第8个字节是主版本号( Major Version)

—常量池:0x0013

常量池可以理解为Class文件之中的资源仓库
常量池中常量的数量是不固定的, 所以在常量池的入口需要放置一项u2类型的数据, 代表常量池容量计数值( constant_pool_count),这个容量计数是从1而不是0开始的

常量池中主要存放两大类常量:

字面量( Literal)

字面量( Literal),如文本字符串、 声明为final的常量值等。

符号引用( Symbolic References)

类和接口的全限定名( Fully Qualified Name)
字段的名称和描述符( Descriptor)
方法的名称和描述符

使用Javap命令输出常量表:

D:\java2>javap -verbose TestClassClassfile /D:/java2/TestClass.class  Last modified 2017-12-15; size 275 bytes  MD5 checksum 73de2fede86f2f34b6780d896a538428  Compiled from "TestClass.java"class TestClass  SourceFile: "TestClass.java"  minor version: 0  major version: 51  flags: ACC_SUPERConstant pool:   #1 = Methodref          #4.#15         //  java/lang/Object."<init>":()V   #2 = Fieldref           #3.#16         //  TestClass.m:I   #3 = Class              #17            //  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               TestClass  #18 = Utf8               java/lang/Object{  TestClass();    flags:    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: invokespecial #1                  // Method java/lang/Object."<init>":()V         4: return      LineNumberTable:        line 1: 0}

—访问标志

两个字节代表访问标志( access_flags) , 这个标志用于识别一些类或者接口层次的访问信息, 包括: 这个Class是类还是接口; 是否定义为public类型; 是否定义为abstract类型; 如果是类的话, 是否被声明为final等

—类索引、 父类索引与接口索引集合

类索引( this_class) 和父类索引( super_class) 都是一个u2类型的数据, 而接口索引集合( interfaces)是一组u2类型的数据的集合, Class文件中由这三项数据来确定这个类的继承关系。 类索引用于确定这个类的全限定名, 父类索引用于确定这个类的父类的全限定名。

–字段表集合

字段表( field_info) 用于描述接口或者类中声明的变量。 字段( field) 包括类级变量以及实例级变量, 但不包括在方法内部声明的局部变量。

字段包含的的信息有: 字段的作用域( public、 private、 protected修饰符) 、 是实例变量还是类变量( static修饰符) 、 可变性( final) 、 并发可见性( volatile修饰符, 是否强制从主内存读写) 、 可否被序列化( transient修饰符) 、 字段数据类型( 基本类型、 对象、 数组) 、 字段名称。

—方法表集合

方法表的结构如同字段表一样, 依次包括了访问标志( access_flags) 、 名称索引( name_index) 、 描述符索引( descriptor_index) 、 属性表集合( attributes) 几项

—属性表集合


4、字节码指令

字节码与数据类型

加载和存储指令

运算指令

运算或算术指令用于对两个操作数栈上的值进行某种特定运算, 并把结果重新存入到操作栈顶。

类型转换指令

类型转换指令可以将两种不同的数值类型进行相互转换, 这些转换操作一般用于实现用户代码中的显式类型转换操作

对象创建与访问指令

对象创建后, 就可以通过对象访问指令获取对象实例或者数组实
例中的字段或者数组元素

操作数栈管理指令

将操作数栈的栈顶一个或两个元素出栈: pop、 pop2。

复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶: dup、 dup2、 dup_x1、 dup2_x1、 dup_x2、dup2_x2。

将栈最顶端的两个数值互换: swap

控制转移指令

条件分支: ifeq、 iflt、 ifle、 ifne、 ifgt、 ifge、 ifnull、 ifnonnull、 if_icmpeq、 if_icmpne、if_icmplt、 if_icmpgt、 if_icmple、 if_icmpge、 if_acmpeq和if_acmpne。

复合条件分支: tableswitch、 lookupswitch。

无条件分支: goto、 goto_w、 jsr、 jsr_w、 ret。

方法调用和返回指令
异常处理指令
同步指令


5、公有设计和私有实现
6、Class文件结构的发展

原创粉丝点击