JVM Class详解之一
首先看Class中包含哪些信息简单的说所有Java文件中有的信息class文件都有,编译器帮我们将java文件转化成了JVM能看懂的class格式而已
Class 概述
Class文件是一组以8位字节为基础的二进制流,各个数据项目按照严格顺序紧凑排列在Class文件中。
所有的16位,32位,64位长度的数据将被构造成2个,4个,8个字节单位来标示。
ClassFile结构
| 类型 | 名称 | 数量 |
|---|---|---|
| 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 | attributes_count | 1 |
| attribute_info | attributes | attributes_count |
class格式说明
- magic:魔数,魔数的唯一作用是确定这个文件是否为一个能被虚拟机所接受的Class文件。魔数值固定为0xCAFEBABE
- minor_version、major_version: 分别为Class文件的副版本和主版本
- constant_pool_count: 常量池计数器,constant_pool_count的值等于constant_pool表中的成员数加1
- constant_pool[]: 常量池,constant_pool是一种表结构,它包含Class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量。常量池不同于其他,索引从1开始到constant_pool_count -1
- access_flags: 访问标志,access_flags是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性
- this_class: 类索引,this_class的值必须是对constant_pool表中项目的一个有效索引值
- super_class: 父类索引
- interfaces_count: 接口计数器,interfaces_count的值表示当前类或接口的直接父接口数量
- interfaces[]: 接口表,interfaces[]数组中的每个成员的值必须是一个对constant_pool表中项目的一个有效索引值,它的长度为interfaces_count
- fields_count: 字段计数器
- fields[]: 字段表,fields[]数组中的每个成员都必须是一个fields_info结构的数据项
- methods_count: 方法计数器
- methods[]: 方法表,methods[]数组中的每个成员都必须是一个method_info结构的数据项
- attributes_count: 属性计数器
- attributes[]: 属性表,attributes表的每个项的值必须是attribute_info结构
废话不多说HelloWorld搞起
public class HelloWorld
{
String str = "";
public String getStr()
{
return str;
}
public void setStr(String str)
{
this.str = str;
}
}
编译成class文件以后,只用javap -verbose HelloWorld.class 指令可以查看当前class的内容
同时使用UE打开class文件
我们来一起看下ClassFile结构
前4个字节为魔数,也就是0xCAFEBABE,这里都是十六进制
魔数后2个字节为副版本号,这里副版本号是0
主版本号0x0033,转为十进制,主版本号是51 标示当前class是通过jdk 1.7编译的,0x32是jdk1.6 0x31是jdk1.5
这两个字节是常量池计数器,常量池的数量为0x001A,转为十进制是26,也就是说常量池索引为1~26
从常量池开始
常量池计数器后面紧跟着就是常量池的内容
所有的常量池项都具有如下通用格式:
cp_info
{
u1 tag;
u1 info[];
}
CONSTANT_Class_info
{
u1 tag;
u2 name_index;
}
CONSTANT_Fieldref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_NameAndType_info
{
u1 tag;
u2 name_index;
u2 descriptor_index;
}
CONSTANT_Utf8_info
{
u1 tag;
u2 length;
u1 bytes[length];
}
CONSTANT_MethodHandle_info
{
u1 tag;
u1 reference_kind;
u2 reference_index;
}


后面的0x07对应tag找到是CONSTANT_Class,标示接下来的是一个class的信息。后面的 00 02 是class的name_index 标示指向常量池的第二个常量。我们再看第二个常量
第二个常量是01开头,我们查看常量类型表中对应是Utf-8,再按照utf-8的结构,后面的00 0A代表了这个utf-8的长度这里长度转换为10进制是11,后面紧跟着utf-8的实际内容

再后面0x 07,是常量池的下一个产量,也是一个class信息,后面跟00 04,name_index执行常量池的
第4个常量。
第4个常量又是utf-8,后面长度为 0x10 十进制为16,接下来的为实际内容
接下来都可以按照此方法分析。
直观结果可以通过javap指令查看

常量池后面紧跟的2个字节是Access Flag,这个表示用于标示类或接口层次的访问信息,如这个Class是类还是接口,是否为public类型,是否定义为abstrace类型。
标志名称 标志值 含义
ACC_PUBLIC 0x0001 是否为public类型
ACC_FINAL 0x0010 是否被声明为final,只有类可设置
ACC_SUPER 0x0020 是否允许使用invokespecial字节码指令,JDK1.2以后编译出来的类这个标志为真
ACC_INTERFACE 0x0200 标识这是一个接口
ACC_ABSTRACT 0x0400 是否为abstract类型,对于接口和抽象类,此标志为真,其它类为假
ACC_SYNTHETIC 0x1000 标识别这个类并非由用户代码产生
ACC_ANNOTATION 0x2000 标识这是一个注解
ACC_ENUM 0x4000 标识这是一个枚举
我们这里0021标示为public Class
接下来的是类索引,父类索引与接口索引集合
this_class,super_class,interfaces_count,interfaces
类索引
为2个字节
这里为00 01,指向常量池中第一个常量,之前我们分析过常量池中第一个常量为Class类型,内容指向第二个常量UTF-8的HelloWorld。
标示当前名为HelloWorld
父类索引
也是2个字节
指向常量中第三个常量,对应内容为java/lang/Object
接口数量

表示接口数量为0
字段表集合
00 01 标示字段数量为1
字段表的格式如下
| 类型 | 名称 | 数量 |
|---|---|---|
| u2 | access_flags | 1 |
| u2 | name_index | 1 |
| u2 | descriptor_index | 1 |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_coun |
accessFlags为 00 00 当时当前字段无修饰符
字段修饰符格式如下
| 标志名称 | 标志值 | 含义 |
|---|---|---|
| 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 |
name_index为 00 05指向常量池中的第五个常量
第5个常量为str,变量名为str
descriptor_index指向常量池第6个变量,为Ljava/lang/String类型
attributes_count(属性计数器,占2字节,0x0000,所以该字段没有额外需要描述的信息)
方法集合

method_count: 00 03 有3个方法
methods:方法表集合
| 类型 | 名称 | 数量 |
|---|---|---|
| u2 | access_flags | 1 |
| u2 | name_index | 1 |
| u2 | descriptor_index | 1 |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_coun |
access flags的定义见下表
| 标志名称 | 标志值 | 含义 |
|---|---|---|
| ACC_PUBLIC | 0x0001 | 字段是否为public |
| ACC_PRIVATE | 0x0002 | 字段是否为private |
| ACC_PROTECTED | 0x0004 | 字段是否为protected |
| ACC_STATIC | 0x0008 | 字段是否为static |
| ACC_FINAL | 0x0010 | 字段是否为final |
| ACC_SYNCHRONIZED | 0x0020 | 字段是否为synchronized |
| ACC_BRIDGE | 0x0040 | 方法是否是由编译器产生的桥接方法 |
| ACC_VARARGS | 0x0080 | 方法是否接受不定参数 |
| ACC_NATIVE | 0x0100 | 字段是否为native |
| ACC_ABSTRACT | 0x0400 | 字段是否为abstract |
| ACC_STRICTFP | 0x0800 | 字段是否为strictfp |
| ACC_SYNTHETIC | 0x1000 | 字段是否为编译器自动产生 |
这里方法access flags 为 00 01 说明方法为public的
name_index为00 07,方法名指向常量中第7个常量方法名为, descriptor_index为常量池第8个常量()V

attributes_count 为 00 01标示这个方法的属性表集合中有一个属性。属性名称为接下来2位0x0009,指向常量池中第9个常量:Code
接下来4位为 00 00 00 3D标示Code属性值的字节长度为3D,接下来为00 02标示该方法的操作数栈的深度最大值为2.
00 01标示该方法的局部变量占用空间为1.
接下来4位00 00 00 0B 为机器编译生成字节码指令的长度为11,后面11个字节就是字节码指令(字节码指令可查询虚拟机字节码指令表),这里字节码指令长度用4个字节标示,所有字节码指令超长Class编译会失败的。
再接下来为 00 00标示Code属性异常表结合为空。
再后面为 00 02,,说明Code带有2个属性, 00 10即为Code属性第一个属性的属性名成指向常量池中第16个常量
接下来的00 00 00 0E 标示LinueNumberTable属性值所占字节长度为15.接下来2位 00 03标示该line number table中有3个line number table表,start pc为 00 00 line number第 00 01个为00 04 第 00 02个为 00 0A
再后面的 00 01又是第二个方法的access flags,接着开始第二个方法。
from: https://yq.aliyun.com/articles/7241?spm=5176.100239.blogcont37179.10.F6pHuW
- 顶
- 0
- 踩
JVM Class详解之一的更多相关文章
- [转]JVM指令详解(上)
作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) 本文主要记录一些JVM指令,便于记忆与查阅. 一.未归类系列A 此系列暂未归类. 指令码 助记符 ...
- JVM内存详解-阅读笔记
- JVM结构详解
JVM 结构详解 JVM 结构图 程序计数器(PC 寄存器) 程序计数器的定义 程序计数器是一块较小的内存空间,是当前线程正在执行的那条字节码指令的地址.若当前线程正在执行的是一个本地方法,那么此时程 ...
- 一篇看懂JVM底层详解,利用class反编译文件了解文件执行流程
JVM之内存结构详解 JVM内存结构 java虚拟机在执行程序的过程中会将内存划分为不同的区域,具体如图1-1所示. 五个区域 JVM分为五个区域:堆.虚拟机栈.本地方法栈.方法区(元空间).程序计数 ...
- JVM组成详解
一.JVM 整体组成 JVM 整体组成可分为以下四个部分: 类加载器(ClassLoader) 运行时数据区(Runtime Data Area) 执行引擎(Execution Engine) 本地库 ...
- Java系列:JVM指令详解(上)(zz)
一.未归类系列A 此系列暂未归类. 指令码 助记符 说明 59:iastore 60:lload 6 //因为str ...
- JVM虚拟机详解
1. 什么是JVM? JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来 ...
- JVM指令详解(上)
指令码 助记符 说明 0x00 nop 什么都不做 0x01 ...
- Java中JVM虚拟机详解
1. 什么是JVM? JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来 ...
随机推荐
- Linux背景知识(1)RedHat和Centos
Redhat有收费的商业版和免费的开源版,商业版的业内称之为RHEL(Red Hat Enterprise Linux)系列, 而这个CentOS(Community ENTerprise Opera ...
- VS2013 工程属性配置
1. 配置属性设置 设置工程编译输出目录 2. 设置第三方库的头文件的位置 3.设置第三方库(动态库或者静态库链接的搜寻的目录) 4.设置链接的第三方库的名称 注: 第三方库的链接可以通过配置文件来实 ...
- python爬虫requests json与字典对象互相转换
import requests import json ''' json.loads(json_str) json字符串转换成字典 json.dumps(dict) 字典转换成json字符串 ''' ...
- 用于水和水蒸汽物性计算的Python模块——iapws
无论是火电还是核电,将能量转化为电能的方式主要还是烧开水,即加热水产生高压蒸汽驱动汽轮机做功再发电.在进行热力循环分析.流动传热计算时,需获得水和水蒸汽的物性参数.网上主流的水蒸汽物性计算程序是上海成 ...
- 【原创】自己动手实现RPC服务调用框架
自己动手实现rpc服务调用框架 本文利用java自带的socket编程实现了一个简单的rpc调用框架,由两个工程组成分别名为battercake-provider(服务提供者).battercake- ...
- Set<E> 接口简明
java. util. Set 接口 Set 接口中的方法和 Collection 中方法一致的.Set 接口取出方式只有一种, 迭代器 . HashSet : 底层数据结构是哈希表,线程 是不同步的 ...
- Java集合框架的四个接口
接口 [四个接口 collection list set map 的区别] collection 存储不唯一的无序的数据 list 存储有序的不唯一的数据 set 存储无序的唯一的数据 m ...
- SQL外连接
1.左外连接 取出左侧关系中所有与右侧关系中任一元组都不匹配的元组,用空值null充填所有来自右侧关系的属性,构成新的元组,将其加入自然连接的结果中 2.右外连接 取出右侧关系中所有与左侧关系中任一元 ...
- R语言-ggplot初级
ggplot2简介: 在2005年开始出现,吸取了基础绘图系统和lattice绘图系统的优点,并利用一个强大的模型来对其进行改进,这一模型基于之前所述的一系列准则, 能够创建任意类型的统计图形 1.导 ...
- Linux下文件权限(一)用户ID和用户组ID
最近在读<unix环境高级编程>,看到文件权限这里比较糊涂,主要设计多个用户ID和用户组ID,包括下面两个: (1)实际用户ID和实际用户组ID:这一部分表示我们究竟是谁.这两个字段在登录 ...