JVM虚拟机Class类文件研究分析
前言
为了研究Class文件,先编写一个最简单的代码:
package com.courage;
public class T0100_ByteCode01 {
}
之所以说最简单,是因为这个类里面任何方法,变量都没有,看看编译之后Class文件的16进制代码:
在解读上面的Class文件(后面没有特殊生命的话都是指16进制)之前,需要先学习几个前置知识,Java 虚拟机规范规定 Class 文件格式采用一种类似与 C 语言结构体的微结构体来存储数据,这种伪结构体中只有两种数据类型:无符号数和表。
- 无符号数属于基本的数据类型,以 u1、u2、u4、u8来分别代表 1 个字节、2 个字节、4 个字节和 8 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8 编码结构构成的字符串值。
- 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以「_info」结尾。表用于描述有层次关系的复合结构的数据,整个 Class 文件就是一张表,它由下表中所示的数据项构成。
有了无符号数这个概念,就可以根据虚拟机规范来解读上面的文件了:
类型 | 名称 | 含义 | 数量 |
---|---|---|---|
u4 | magic | 魔数,不变 | 1 |
u2 | minor_version | 小版本号:JDK 8_255u中的255u | 1 |
u2 | major_version | 大版本号,JDK 8_255u中的8 | 1 |
u2 | constant_pool_count | 常量池数量 | 1 |
cp_info | constant_pool | 常量池 | constant_pool_count-1 |
u2 | access_flags | 访问修饰符 public static 等 | 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 | attributes_count | 属性数量 | 1 |
attribute_info | attributes | 属性 | attributes_count |
所有的Class文件里面的属性都按照上表的规则排序,中间没有空行或其他转义字符。哪个字节代表什么含义,长度是多少,先后顺序如何都是被严格限制的,不允许有任何改变。
魔数与 Class 文件版本
每个 Class 文件的头 4 个字节称为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接收的 Calss 文件。之所以使用魔数而不是文件后缀名来进行识别主要是基于安全性的考虑,因为文件后缀名是可以随意更改的。Class 文件的魔数值为「0xCAFEBABE」。
紧接着魔数的 4 个字节存储的是 Class 文件的版本号:第 5 和第 6 两个字节是次版本号(Minor Version),第 7 和第 8 个字节是主版本号(Major Version)。高版本的 JDK 能够向下兼容低版本的 Class 文件,虚拟机会拒绝执行超过其版本号的 Class 文件。
常量池
主版本号之后是常量池入口,常量池可以理解为 Class 文件之中的资源仓库,它是 Class 文件结构中与其他项目关联最多的数据类型,也是占用 Class 文件空间最大的数据项目之一,同是它还是 Class 文件中第一个出现的表类型数据项目。
因为常量池中常量的数量是不固定的,所以在常量池入口需要放置一个 u2 类型的数据来表示常量池的容量「constant_pool_count」,和计算机科学中计数的方法不一样,这个容量是从 1 开始而不是从 0 开始计数。之所以将第 0 项常量空出来是为了满足后面某些指向常量池的索引值的数据在特定情况下需要表达「不引用任何一个常量池项目」的含义,这种情况可以把索引值置为 0 来表示。
Class 文件结构中只有常量池的容量计数是从 1 开始的,其它集合类型,包括接口索引集合、字段表集合、方法表集合等容量计数都是从 0 开始。
常量池中主要存放两大类常量:字面量和符号引用。
字面量比较接近 Java 语言层面的常量概念,如字符串、声明为 final 的常量值等。
符号引用属于编译原理方面的概念,包括了以下三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
常量池17种数据类型的结构表
访问标志
紧接着常量池之后的两个字节代表访问标志(access_flag),这个标志用于识别一些类或者接口层次的访问信息,包括这个 Class 是类还是接口;是否定义为 public 类型;是否定义为 abstract 类型;如果是类的话,是否被申明为 final 等。具体的标志位以及标志的含义见下表:
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为 public 类型 |
ACC_FINAL | 0x0010 | 是否被声明为 final,只有类可设置 |
ACC_SUPER | 0x0020 | 是否允许使用 invokespecial 字节码指令的新语意,invokespecial 指令的语意在 JKD 1.0.2 中发生过改变,微聊区别这条指令使用哪种语意,JDK 1.0.2 编译出来的类的这个标志都必须为真 |
ACC_INTERFACE | 0x0200 | 标识这是一个接口 |
ACC_ABSTRACT | 0x0400 | 是否为 abstract 类型,对于接口或者抽象类来说,此标志值为真,其它类值为假 |
ACC_SYNTHETIC | 0x1000 | 标识这个类并非由用户代码产生 |
ACC_ANNOTATION | 0x2000 | 标识这是一个注解 |
ACC_ENUM | 0x4000 | 标识这是一个枚举 |
access_flags 中一共有 16 个标志位可以使用,当前只定义了其中的 8 个,没有使用到的标志位要求一律为 0。
类索引、父类索引与接口索引集合
类索引(this_class)和父类索引(super_class)都是一个 u2 类型的数据,而接口索引集合(interfaces)是一组 u2 类型的数据集合,Class 文件中由这三项数据来确定这个类的继承关系。
- 类索引用于确定这个类的全限定名
- 父类索引用于确定这个类的父类的全限定名
- 接口索引集合用于描述这个类实现了哪些接口
字段表集合
字段表集合(field_info)用于描述接口或者类中声明的变量。字段(field)包括类变量和实例变量,但不包括方法内部声明的局部变量。下面我们看看字段表的结构:
类型 | 名称 | 数量 |
---|---|---|
u2 | access_flag | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
字段修饰符放在 access_flags 中,它与类中的 access_flag 非常相似,都是一个 u2 的数据类型。
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 字段是否为 public |
ACC_PRIVATE | 0x0002 | 字段是否为 private |
ACC_PROTECTED | 0x0004 | 字段是否为 protected |
ACC_STATIC | 0x0008 | 字段是否为 static |
ACC_FINAL | 0x0010 | 字段是否为 final |
ACC_VOLATILE | 0x0040 | 字段是否为 volatile |
ACC_TRANSIENT | 0x0080 | 字段是否为 transient |
ACC_SYNTHETIC | 0x1000 | 字段是否由编译器自动生成 |
ACC_ENUM | 0x4000 | 字段是否为 enum |
方法表集合
Class 文件中对方法的描述和对字段的描述是完全一致的,方法表中的结构和字段表的结构一样。
因为 volatile 关键字和 transient 关键字不能修饰方法,所以方法表的访问标志中没有 ACC_VOLATILE 和 ACC_TRANSIENT。与之相对的,synchronizes、native、strictfp 和 abstract 关键字可以修饰方法,所以方法表的访问标志中增加了 ACC_SYNCHRONIZED、ACC_NATIVE、ACC_STRICTFP 和 ACC_ABSTRACT 标志。
对于方法里的代码,经过编译器编译成字节码指令后,存放在方法属性表中一个名为「Code」的属性里面。
属性表集合
在 Class 文件、字段表、方法表中都可以携带自己的属性表(attribute_info)集合,用于描述某些场景专有的信息。
属性表集合不像 Class 文件中的其它数据项要求这么严格,不强制要求各属性表的顺序,并且只要不与已有属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java 虚拟机在运行时会略掉它不认识的属性。
下面就可以对类文件逐行分析了:
JVM虚拟机Class类文件研究分析的更多相关文章
- JVM虚拟机查找类文件的顺序
JVM查找类文件的顺序: 在doc下使用set classpath=xxx, 如果没有配置classpath环境变量,JVM只在当前目录下查找要运行的类文件. 如果配置了classpath环境,JVM ...
- JVM(五) class类文件的结构
概述 class类文件的结构可见下面这样图(出处见参考资料),可以参照下面的例子,对应十六进制码,找出找出相应的信息. 其中u2 , u4 表示的意思是占用两个字节和占用四个字节,下面我们将会各项说明 ...
- JVM虚拟机 - Class类文件结构
概述 Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎都是程序运行的必要数据 ...
- 干货分享丨jvm系列:dump文件深度分析
摘要:java内存dump是jvm运行时内存的一份快照,利用它可以分析是否存在内存浪费,可以检查内存管理是否合理,当发生OOM的时候,可以找出问题的原因.那么dump文件的内容是什么样的呢? JVM ...
- 深入理解Java虚拟机(七)——类文件结构
Java的无关性 由于计算机领域中有很多操作系统和硬件平台同时在竞争,所以,很多编程语言的程序设计会与其运行的平台和操作系统产生耦合,这样就大大增加了程序员的工作,为了适应不同的平台,需要修改很多代码 ...
- 【JVM虚拟机】(6)---深入理解Class中访问标志、类索引、父类索引、接口索引
JVM(6)访问标志,类索引 上一篇博客讲[JVM虚拟机](5)---深入理解JVM-Class中常量池 我们知道一个class文件正常可以分为7个部分: 魔数与class文件版本 常量池 访问标志 ...
- Java虚拟机JVM学习04 类的初始化
Java虚拟机JVM学习04 类的初始化 类的初始化 在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值. 在程序中,静态变量的初始化有两种途径: 1.在静态变量的声明处进行初始 ...
- Java虚拟机JVM学习02 类的加载概述
Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...
- 【JVM】Java中的JavaCore/HeapDump文件及其分析方法
产生时间 Java程序运行时,有时会产生JavaCore及HeapDump文件,它一般发生于Java程序遇到致命问题的情况下. 有时致命问题发生后,Java应用不会死掉,还能继续运行: 但有时致命问题 ...
随机推荐
- 仅4步,就可通过SQL进行分布式死锁的检测与消除
摘要:本文主要介绍在 GaussDB(DWS) 中,如何通过 SQL 语句,对分布式死锁进行检测和恢复. 分布式数仓应用场景中,我们经常遇到数据库系统 hang 住的问题,所谓 hang 是指虽然数据 ...
- 对象的克隆(clone方法)
1.深拷贝与浅拷贝 浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象.深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象. 2.深拷贝和浅拷贝的 ...
- 数据库1 --- > 数据库概念、安装、卸载
数据库概念 为什么学习数据库?1.web中的数据量非常大:2. 数据不方便存储和管理 什么是数据库: 用于存储和管理数据的仓库 数据库的特点: 数据可以实现持久化存储,其实数据库就是一个文件系统. ...
- 对HTTP请求接口资源下载时间过长的问题分析
问题描述 我司某产品线有指定业务接口customQuery在线上环境中,与首页一起打开时下载数据的时间明显过长(平均可以达到2s) 注: "与首页一起打开" 的含义是指用户进入WE ...
- 容器编排系统K8s之Pod Affinity
前文我们了解了k8s上的NetworkPolicy资源的使用和工作逻辑,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14227660.html:今天我们来聊 ...
- CentOS8设置网络镜像安装源
CentOS8通过引导盘+网络镜像镜像源安装系统,设置网络镜像安装源为: mirrors.aliyun.com/centos/8/BaseOS/x86_64/os
- [ABP教程]第三章 创建、更新和删除图书
Web应用程序开发教程 - 第三章: 创建,更新和删除图书 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以 ...
- .NET Core 中的日志与分布式链路追踪
目录 .NET Core 中的日志与分布式链路追踪 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory IL ...
- 使用python做一个IRC在线下载器
使用python做一个IRC在线下载器 1.开发流程 2.软件流程 3.开始 3.0 准备工作 3.1寻找API接口 3.2 文件模块 3.2.1 选择文件弹窗 3.2.2 提取文件名 3.2.2.1 ...
- TeamView WaitforConnectFailed错误原因
更新到最新版本并重启如下服务 检查TCP IPV4是否选中