虚拟机Class文件结构笔记

>>Java语言的平台无关性

Java是与平台无关的语言,“一次编写,到处运行”,
这一方面依赖于Java源代码编译后生成的存储字节码的文件,即Class文件是语言和平台无关的;

另一方面依赖于Java虚拟机的实现。

Java虚拟机并不关心Class的来源是什么语言,只要它符合一定的结构,就可以在Java中运行。
Java语言中的各种变量、关键字和运算符的语义最终都是由多条字节码命令组合而成的,
因此字节码命令所能提供的语义描述能力肯定会比Java语言本身更强大,
这便为其他语言实现一些有别于Java的语言特性提供了基础,而且这也正是在类加载时要进行安全验证的原因。

>>Class类文件结构

class文件是一种8位字节的二进制流文件, 各个数据项按顺序紧密的从前向后排列, 相邻的项之间没有间隙, 这样可以使得class文件非常紧凑, 体积轻巧, 可以被JVM快速的加载至内存, 并且占据较少的内存空间。
我们的Java源文件, 在被编译之后, 每个类(或者接口)都单独占据一个class文件, 并且类中的所有信息都会在class文件中有相应的描述, 由于class文件很灵活, 它甚至比Java源文件有着更强的描述能力。

class文件中存在以下数据项:

 

 

类型 名称 数量
u4 magic 1
u2 minor_version 1
u2 major_version 1
u2 constant_pool_count 1
cp_info constant_pool constant_pool_count - 1
u2 access_flags 1
u2 this_class 1
u2 super_class 1
u2 interfaces_count 1
u2 interfaces interfaces_count
u2 fields_count 1
field_info fields fields_count
u2 methods_count 1
method_info methods methods_count
u2 attribute_count 1
attribute_info attributes attributes_count

 无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会在其前面使用一个前置的容量计数器来记录其数量,而便跟着若干个连续的数据项,称这一系列连续的某一类型的数据为某一类型的集合,如:fields_count个field_info表数据便组成了方法表集合。

(1)魔数和版本号 magic minor_version&major_version
每个Class文件的头4个字节称为魔数(magic),它的唯一作用是判断该文件是否为一个能被虚拟机接受的Class文件。它的值固定为0xCAFEBABE。
紧接着magic的4个字节存储的是Class文件的次版本号和主版本号,一般情况下, 高版本的JVM能识别低版本的javac编译器编译的class文件, 而低版本的JVM不能识别高版本的javac编译器编译的class文件。 如果使用低版本的JVM执行高版本的class文件, JVM会抛出java.lang.UnsupportedClassVersionError 。

(2)常量池计数和常量池 constant_pool_count&constant_pool
常量池是class文件中的一项非常重要的数据。 常量池中存放了文字字符串, 常量值, 当前类的类名, 字段名, 方法名, 各个字段和方法的描述符, 对当前类的字段和方法的引用信息, 当前类中对其他类的引用信息等等。
常量池中主要存放两大类常量:字面量和符号引用。字面量比较接近于Java层面的常量概念,如文本字符串、被声明为final的常量值等。而符号引用总结起来则包括了下面三类常量:

类和接口的全限定名(即带有包名的Class名)
字段的名称和描述符(private、static等描述符)
方法的名称和描述符(private、static等描述符)

(3) 访问标志 access_flag

在常量池结束之后,紧接着的2个字节代表访问标志(access_flag),这个标志用于识别一些类或接口层次的访问信息,包括:这个Class是类还是接口,是否定义为public类型,abstract类型,如果是类的话,是否声明为final,等等。每种访问信息都由一个十六进制的标志值表示,如果同时具有多种访问信息,则得到的标志值为这几种访问信息的标志值的逻辑或。

(4) 类索引和父类索引 接口索引集合 this_class、super_class、interfaces
类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,而接口索引集合(interfaces)则是一组u2类型的数据集合,Class文件中由这三项数据来确定这个类的继承关系。类索引、父类索引和接口索引集合都按照顺序排列在访问标志之后,类索引和父类索引两个u2类型的索引值表示,它们各自指向一个类型为COMNSTANT_Class_info的类描述符常量,通过该常量中的索引值找到定义在COMNSTANT_Utf8_info类型的常量中的全限定名字符串。而接口索引集合就用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句(如果这个类本身是个接口,则应当是extend语句)后的接口顺序从左到右排列在接口的索引集合中。

(5)字段表 fields
字段表(field_info)用于描述接口或类中声明的变量。字段包括了类级变量或实例级变量,但不包括在方法内声明的变量。字段的名字、数据类型、修饰符等都是无法固定的,只能引用常量池中的常量来描述。

(6)方法表 methods
方法表(method_info)的结构与属性表的结构相同,方法里的Java代码,经过编译器编译成字节码指令后,存放在方法属性表集合中一个名为“Code”的属性里。
与字段表集合相对应,如果父类方法在子类中没有被覆写,方法表集合中就不会出现来自父类的方法信息。但同样,有可能会出现由编译器自动添加的方法,最典型的便是类构造器“<clinit>”方法和实例构造器“<init>”方法。
在Java语言中,要重载一个方法,除了要与原方法具有相同的简单名称外,还要求必须拥有一个与原方法不同的特征签名,特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,也就是因为返回值不会包含在特征签名之中,因此Java语言里无法仅仅依靠返回值的不同来对一个已有方法进行重载。

(7)属性表 attributes
属性表(attribute_info)在前面已经出现过多系,在Class文件、字段表、方法表中都可以携带自己的属性表集合,以用于描述某些场景专有的信息。

>>final、static、static final修饰的字段赋值的区别

static修饰的字段在类加载过程中的准备阶段被初始化为0或null等默认值,而后在初始化阶段(触发类构造器<clinit>)才会被赋予代码中设定的值,如果没有设定值,那么它的值就为默认值。
final修饰的字段在运行时被初始化(可以直接赋值,也可以在实例构造器中赋值),一旦赋值便不可更改;
static final修饰的字段在Javac时生成ConstantValue属性,在类加载的准备阶段根据ConstantValue的值为该字段赋值,它没有默认值,必须显式地赋值,否则Javac时会报错。可以理解为在编译期即把结果放入了常量池中。

 

时间: 2024-09-27 11:08:53

虚拟机Class文件结构笔记的相关文章

《深入理解Java虚拟机》学习笔记

自动内存管理机制 第2章 垃圾收集器与内存分配策略 1.Java虚拟机在执行java程序时会把它所管理的内存会分为若干个不同的数据区域, 这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在, 有些区域则是在以来用户线程的启动和结束而建立和销毁. 根据<Java虚拟机规范>,包括以下几个运行时数据区域: //此处应有类图,但是画起来太麻烦! 程序计数器(Program Counter Register) 方法区(Method Area) 虚拟机栈(VM Stack)

《深入理解Java虚拟机》读书笔记

背景 并发处理的广泛应用是使得Amdahl定律代替摩尔定律成为计算机性能发展的源动力的根本原因,也是人类压榨计算机运算能力最有力的武器 Amdahl 定律通过系统中的并行化与串行化的比重来描述多处理器系统能获得的运算加速能力. 摩尔定律则用于描述处理器晶体管数量与运行效率之间的发展关系. 这两个定律的更替代表了近年来硬件发展从追求处理器频率到追求多核心并行处理的发展过程. 高效并发 物理机上的并发解决方案 在当前这个多核处理器时代,"让计算机并发执行若干个运算任务"和"更充分

转《深入理解Java虚拟机》学习笔记之最后总结

编译器 Java是编译型语言,按照编译的时期不同,编译器可分为: 前端编译器:其实叫编译器的前端更合适些,它把*.java文件转变成*.class文件,如Sun的Javac.Eclipse JDT中的增量式编译器ECJ: JIT编译器:虚拟机的后端运行期编译器(Just In Time Compiler),它把字节码转变成机器码,如HotSpot VMd C1.C2编译器: AOT编译器:静态提前编译器(Ahead Of Time Compiler),它直接把*.java文件编译成本地机器码,如

KVM虚拟机技术学习总结_Kvm

最近在学习KVM,进程不算太快,近期整理了一下KVM虚拟机技术学习笔记,现在就分享给大家,也给大家做个参考.有需要的朋友可以来了解一下. KVM虚拟机的管理主要是通过virsh命令对虚拟机进行管理. 1.  查看KVM虚拟机配置文件及运行状态 (1) KVM虚拟机默认配置文件位置: /etc/libvirt/qemu/ autostart目录是配置kvm虚拟机开机自启动目录.   (2) virsh命令帮助 # virsh -help 或直接virsh命令和,再执行子命令.如下所示. [root

运维前线:一线运维专家的运维方法、技巧与实践3.3 利用批处理与Shell脚本简化逻辑节点的搬迁

3.3 利用批处理与Shell脚本简化逻辑节点的搬迁 3.3.1 逻辑节点切换脚本的思路 众所周知,对于服务器的搬迁,不只会涉及物理层面的设备搬迁.在物理设备搬迁到新机房后,往往还需要对虚拟机中的多种参数进行相关调整. 如果管理的服务器台数比较少的话,则可以采用手工设置的方式逐一更改参数.但是,如果手中管理的Windows和Linux虚拟机有数千台的话,那么这些繁琐的参数调整一定会让你头疼不已.其中需要调整的参数如下: IP GATEWAY DNS WSUS NTP HOSTS 也许大家要问,像

jvm开发笔记3&amp;#8212;java虚拟机雏形

作者:王智通   一.背景 笔者希望通过自己动手编写一个简单的jvm来了解java虚拟机内部的工作细节毕竟hotsopt以及android的dalvik都有几十万行的c代码级别. 在前面的2篇开发笔记中已经实现了一个class文件解析器和一个java反汇编器 在这基础上 java虚拟机的雏形也已经写好.还没有内存管理功能 没有线程支持.它能解释执行的指令取决于我的java语法范围 在这之前我对java一无所知 通过写这个jvm顺便也把java学会了 它现在的功能如下 1.java反汇编器 山寨了

java虚拟机学习笔记

笔记 1.编译顺序:                 编译器                     虚拟机      虚拟机          java源文件*.java------->字节码*.class------>类装载器--->执行引擎 一个.class文件只能包含一个类或接口.因此.java文件中定义了多少类,编译时就会生成多少.class文件(内部类不算). 2.java程序可以选择两种方式访问底层系统,由程序员选择:(1).通过java程序调用javaapi调用本地方法,

java虚拟机学习笔记1

笔记 1.编译顺序:                 编译器                     虚拟机      虚拟机          java源文件*.java------->字节码*.class------>类装载器--->执行引擎 一个.class文件只能包含一个类或接口.因此.java文件中定义了多少类,编译时就会生成多少.class文件(内部类不算). 2.java程序可以选择两种方式访问底层系统,由程序员选择:(1).通过java程序调用javaapi调用本地方法,

java虚拟机学习笔记2

笔记 11.数组数组也是类的对象.具有相同类型和维数的数组属于同一个类(不管长度只看维数).数组的长度属于对象实例.多维数组也是一维数组.如二 维数组,即为一个一维数组,该一维数组的每个元素是一个数组的引用.数组和普通对象一样也存储在堆中.数组名为数组的引用,通过索引即数组标号来访问数组内容. 12.异常在java栈帧的帧数据区内保存有针对该方法的异常表的引用.异常表记载了该方法的字节码(*.class)受catch子句保护的范围(即try子句里的 字节码).当某个方法抛出异常时,虚拟机在对应的