深入理解java虚拟机-第六章
第6章 类文件
6.3 Class类文件的结构
Class文件是一组以8位字节为基础单位的二进制流。
Class文件格式采用一种类似C语言结构伪结构存储数据,这种伪结构中只有两种数据类型:无符号数和表
无符号数属于基本的数据类型以u1,u2,u4,u8来分别代表一个字节、2个字节,4个字节、8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字条串值。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表习惯性地以 _info 结尾。
表 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 | interface_count | 1 |
u2 | interfaces | interfaces_count |
u2 | fields_count | 1 |
field_info | fields | field_count |
u2 | methods_count | 1 |
method_info | methods | methods_count |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
1 魔数与Class的版本
public class Test { private int m; private int inc(){
return m + 1;
} }
如上类编译后的类文件,通过UE打开后:
前四位为魔数 : CAFEBABE
第五六位 ( 00 00 )为 次版本号( Minor Version)
第7 8 位 (00 32) 为主版本号( Major Version) 十六进制 0x0032为十进制 50 表明可以被JDK1.6以上的版本执行(JDK1.1能支持45.0~45.65535,JDK1.2能支持 45.0~46.65535,JDK1.5一般为48, JDK1.6一般为50,需确认)
2 常量池
紧接着主版本后是 常量池个数u2 与常量池 cp_info, 常量池可以理解为Class文件之中的资源仓库。常量池中常量的数量为常量池个数-1。
常量池中主要存放两在类常量:字面量(Literal)和符号引用(Symbolic References)。每一个常量都是一个表,JDK1.7共有14种不同的表结构(CONSTANT_Utf8_info(1), CONSTANT_Integer_info(3),CONSTANT_Float_info(4),CONSTANT_Long_info(5),CONSTANT_Double_info(6), CONSTANT_Class_info(7)等),每一种常量都有自己的类型,每种类型有一个共同的特点,每个结构的第一位是一个u1类型的标志位(tag)。
如图所示:常量池个数 (00 16) 为16进制0x0016转为10进制22,代表常量池中有21个常量。
0x07为第一个常量的类型中的 tag ,查到类型为CONSTANT_Class_info(7) , CONSTANT_Class_info的结构如下:
类型 | 名称 | 数量 |
u1 | tag | 1 |
u2 | name_index | 1 |
0x07后面跟两个字节: 0x0002表示 name_index; name_inde是一个索引值,它指赂常量池中一个CONSTANT_Utf8_info的常量,此常量代表了这个类(或者接口)的全限定名。此处为 0x0002表示指向了常量池中的第二项常量。
下一字段为 01,为第二个常量的tag,0x01表示该常量为CONSTANT_Utf8_info类型,CONSTANT_Utf8_info类型结构如下:
类型 | 名称 | 数量 |
u1 | tag | 1 |
u2 | length | 1 |
u1 | bytes | length |
length表示该UTF-8编码的字条长度是多少字节,如下图
0x0004表示字节长度为 4字节 0x54657374表示为 Test
剩余的19个常量可以使用javap工具来分析:
21个常量中有很多是没有在Java代码中直接出现过的,它们会被后面即将讲到的字段表(field_info),方法表(method_info),属性表(attribute_info)引用到。
3 访问标志
再往下两字节为访问标志 access_flag,用于识别类或者接口层次的访问信息 包括:这个Class是类还是接口,是否定义public类型
4 类索引 父类索引与接口索引集合
类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,而接口索引集合(interfaces)是一组u2类型的数据的集合。
他们的数值都会指向常量池中的位置:
this_class的值为0x0001指向常量池中的第一个 const #1=class #2
super_class的值为0x0003指向常量池中的3
5字段表集合
用于描述接口或类中声明的变量,可以包括的信息有:字段的作用域(public, private, protected) 是实例变量还是类变量(static修饰符),可变性(final),并发可见性(volatile修饰,是否强制从主内在读写),可否被序列化,字段数据类型(基本类型、对象、数组),字段名称等。
字段结构:访问标志(access_flags) u2, 名称索引( name_index) u2, 描述符索引(descriptor_index)u2,属性个数(attributs_count) u2, 属性表集合(attributs) attributs_count
描述符的作用是用来描述字段的数据类型、方法参数列表(包括数量、类型以及顺序)和返回值。基本数据类型(byte,char, double, float, int, long, short, boolean)以及代表无返回值的void类型都用一个大写字符来表示,而对象类型用字符L加对象的全限定名来表示。
B-bype, C-char, D-double, F-float, I-int, J-long, S-short,Z-boolean, V-void, L-对象类型
对于数组,每一组度前置一个[来描述 java.lang.String[][] --> [[java/lang/String; int[] ---> [I
6方法表集合
与字段表集合基本相同,仅在访问标志和属性表集合的可选荐中有区别。
方法的java代码经过编译器编译成字节码指令后,存放在方法属性表集合中一个名为 Code的属性里面。
7 属性表集合
在Class文件、字段表、方法表都可以携带自己的属性表集合,用于描述某些场景专有的信息
6.4 字节码指令简介
1 字节码与数据类型
在java虚拟机的指令集中,大多数指令都包含了其操作所对应的数据类型信息,例如:iload指令用于从局部变量表中加载int型数据到操作数栈.
操作码助记符中都有特殊的字条来表明专门为哪种数据类型服务, i:int, l:long, s:short, b:byte, c:char, f:float, d:double, a:reference.
Java虚拟机的操作码长度只有一个字节,如果每一种数据类型相关的指令都支持Java虚拟机所有运行时数据类型的话,那指令的数据恐怕就会超过一个字节所能表示的数量范围了。因此,Java虚拟机的指令集对于特定的操作只提供了有限的类型相关指令去支持它。
2 加载和存储指令
加载和存储指令用于将数据在栈帧中的局部变量表和操作数据栈之间来回传输
将一个局部变量加载到操作栈: iload, iload_<n>, lload, fload .....
将一个数值从操作数栈存储到局部变量表: istore, istore_<n>....
将一个常量加载到操作数栈: bipush, sipush, ldc.....
扩充局部变量表的访问索引的指令: wide.
3 运算指令
运算或算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作栈顶。由于没有直接支持byte,short, chart和boolean类型的算术指令,对于这类数据的运算,应使用操作int类型的指令代替。
加法: idadd, ladd, fadd, dadd.
减法:isub, lsub, fsub, dsub,
乘法:imul, lmul, fmul, dmul
除法:idiv, ldiv, fdiv, ddiv
求余: irem, lrem, frem, drem
取反: ineg, lneg, fneg, dneg
位移: ishl, ishr, iushr, lshl, lshr, lushr
按位或指令: ior, lor
按位与指令: iand, land
按位异或指令: ixor, lxor
局部变量自增指令: iinc
比较指令: dcmpg, dcmpl, fcmpg, fcmpl, lcmp
4 类型转换指令
类型转换指令可以将两种不同的数值类型进行想到转换
Java虚拟机直接支持(即转换无需显式的转换指令)以下数据类型的宽化类型转换(Widening Numeric Conversions,即小范围类型向大范围类型的安全转换。 1、int类型到long, float 或者 double类型, 2、long类型到float, double类型, 3、float类型到double类型
相对的,处理窄化类型转换( Narrowing Numeric Conversions)时,必须显式地使用转换指令来完成,这些转换指令包括:i2b, i2c, i2s........
5 对象创建与访问指令
创建类实例的指令: new
创建数组的指令: newarray, anewarray, multianewarray
访问类字段(static字段)和实例字段(非static字段,或者称为实例变量)的指令: getfield, putfield, getstatic, putstatic
把一个数组元素加载到操作数栈的指令: baload, caload, saload, iaload, laload......
将一个操作数栈的值存储到数组元素中的指令: bastore, castore,sastore....
取数组长度的指令: arraylength
检查类实例类型的指令: instanceof, checkcast
6 操作数栈管理指令
java虚拟机提供了直接操作操作数栈的指令。
将操作数栈的栈顶一个或两个元素出栈: pop, pop2
复制栈顶一个或两个数值并将复制值或双份的复制值 重新压入栈顶:dup, dup2, dpu_x1....
将栈最顶端的两个数值互换: swap
7 控制转移指令
条件分支:ifeq, iflt, ifle....
复合条件分支: tableswitch, lookpuswitch
无条件分支: goto, toto_w....
8方法调用和返回指令
invokevirtual指令用于调用对象的实例方法。
invokeinterface 用于调用接口方法
invokespecial
invokestatic
invokedynamic 用于在运行时动态解析出调用点限定符所引用的方法
9异常处理
在Java程序中显式异常的操作(throw语句)都由athrow指令实现
10 同步指令
深入理解java虚拟机-第六章的更多相关文章
- 深入理解java虚拟机-第13章-线程安全与锁优化
第十三章 线程安全与锁优化 线程安全 java语言中的线程安全 1 不可变.Immutable 的对象一定是线程安全的 2 绝对线程安全 一个类要达到不管运行时环境如何,调用者都不需要额外的同步措施, ...
- 深入理解Java虚拟机-第1章-走进Java-读书笔记
第 1 章 走近 Java 前言 Java 的技术体系主要是由支撑 Java 程序运行的虚拟机.为各开发领域提供接口支持的 Java API.Java 编程语言及许许多多的第三方 Java 框架(如 ...
- 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域
- 深入理解java虚拟机_第二章_读书笔记
1.本章内容目录: 概述 运行时数据区域 程序计数器 java虚拟机栈 本地方法栈 java堆 方法区 运行时常量池 直接内存 HotSpot虚拟机对象探秘 对象的创建 对象的内存布局 对象的访问定位 ...
- 深入理解java虚拟机(六)字节码指令简介
Java虚拟机指令是由(占用一个字节长度.代表某种特定操作含义的数字)操作码Opcode,以及跟随在其后的零至多个代表此操作所需参数的称为操作数 Operands 构成的.由于Java虚拟机是面向操作 ...
- 深入理解Java虚拟机 第三章 垃圾收集器 笔记
1.1 垃圾收集器 垃圾收集器是内存回收的具体实现.以下讨论的收集器是基于JDK1.7Update14之后的HotSpot虚拟机.这个虚拟机包含的所有收集器有: 上图展示了7种作用于不同分代的收集 ...
- 深入理解java虚拟机-第七章
第7章 虚拟机类加载机制 类的加载的时机 加载 Loading, 连接 Linking(验证 Verfiication, 准备Preparation, 解析 Resolution) 初始化 Initi ...
- 深入理解java虚拟机-第四章
第4章 虚拟机性能监按与故障处理工具 jps 虚拟机进程状况工具 jstat 虚拟机统计信息监视工具 JVM Statistics Monitoring Tool jstat [ option vmi ...
- 《深入理解JAVA虚拟机》----------第二章 JAVA内存区域与内存溢出异常,笔记(下)
2. HotSpot虚拟机对象探秘 2.1 对象的创建 虚拟机遇到一条New指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初 ...
随机推荐
- $用python-docx模块读写word文档
工作中会遇到需要读取一个有几百页的word文档并从中整理出一些信息的需求,比如产品的API文档一般是word格式的.几百页的文档,如果手工一个个去处理,几乎是不可能的事情.这时就要找一个库写脚本去实现 ...
- 多种数据库之间 update的不同
sql server update a set a.gqdltks=b.gqdltks,a.bztks=b.bztks from landleveldata a,gdqlpj b where a.GE ...
- Django 知识补漏单例模式
Django 知识补漏单例模式 单例模式:(说白了就是)创建一个类的实例.在 Python 中,我们可以用多种方法来实现单例模式: 1.文件导入的形式(常用) s1.py class Foo(obje ...
- 20145201 《Java程序设计》第一周学习总结
# 20145201 <Java程序设计>第一周学习总结 ## 教材学习内容总结 万事开头难,终于开始学习了Java.寒假的时候看到老师的要求确实有点慌,但是这周翻开书,从书本知识第一行学 ...
- HP P420i Smart Array
http://blog.mpecsinc.ca/2013/02/hp-p420i-smart-array-adding-4-new-disks.html https://techzone.ergon. ...
- tomcat集群基于Nginx——共享同一个应用
1.首先准备两个tomcat,也可以一个复制两个.和一个Nginx tomcat官方下载连接——安装版&绿色版 Nginx官网下载链接:http://nginx.org/download/ 博 ...
- mysql——主键自动增长&唯一索引
首先说一下主键和唯一索引的区别 主键:一个数据库的一张表有且仅有一个主键,而且主键不能重复 唯一索引:一个数据库的一张表上唯一索引可以有多个,只是所在唯一索引上的值不能重复,这一点和主键一样 下面我们 ...
- scala学习手记37 - 容器的使用
这次统一看一下scala中容器类的几个方法. Set filter()方法 filter()方法用来从Set中过滤获取含有指定特征的元素.示例代码如下: val colors1 = Set(" ...
- scala学习手记9 - =和==
= 赋值运算 scala的赋值运算和java的有着很大的不同.如a=b这样的赋值运算,在Java中返回值是a的值,在scala中返回的则是Unit(Unit是值类型,全局只存在唯一的值,即(),通常U ...
- DatePicker日期与时间控件
DatePicker日期与时间控件 一.简介 二.方法 最日常的使用方法了 日期控件DatePicker 时间控件TimePicker 月份从0开始 三.代码实例 效果图: 代码: fry.Activ ...