第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虚拟机-第六章的更多相关文章

  1. 深入理解java虚拟机-第13章-线程安全与锁优化

    第十三章 线程安全与锁优化 线程安全 java语言中的线程安全 1 不可变.Immutable 的对象一定是线程安全的 2 绝对线程安全 一个类要达到不管运行时环境如何,调用者都不需要额外的同步措施, ...

  2. 深入理解Java虚拟机-第1章-走进Java-读书笔记

    第 1 章 走近 Java 前言 Java 的技术体系主要是由支撑 Java 程序运行的虚拟机.为各开发领域提供接口支持的 Java API.Java 编程语言及许许多多的第三方 Java 框架(如 ...

  3. 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常

    第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域  

  4. 深入理解java虚拟机_第二章_读书笔记

    1.本章内容目录: 概述 运行时数据区域 程序计数器 java虚拟机栈 本地方法栈 java堆 方法区 运行时常量池 直接内存 HotSpot虚拟机对象探秘 对象的创建 对象的内存布局 对象的访问定位 ...

  5. 深入理解java虚拟机(六)字节码指令简介

    Java虚拟机指令是由(占用一个字节长度.代表某种特定操作含义的数字)操作码Opcode,以及跟随在其后的零至多个代表此操作所需参数的称为操作数 Operands 构成的.由于Java虚拟机是面向操作 ...

  6. 深入理解Java虚拟机 第三章 垃圾收集器 笔记

    1.1   垃圾收集器 垃圾收集器是内存回收的具体实现.以下讨论的收集器是基于JDK1.7Update14之后的HotSpot虚拟机.这个虚拟机包含的所有收集器有: 上图展示了7种作用于不同分代的收集 ...

  7. 深入理解java虚拟机-第七章

    第7章 虚拟机类加载机制 类的加载的时机 加载 Loading, 连接 Linking(验证 Verfiication, 准备Preparation, 解析 Resolution) 初始化 Initi ...

  8. 深入理解java虚拟机-第四章

    第4章 虚拟机性能监按与故障处理工具 jps 虚拟机进程状况工具 jstat 虚拟机统计信息监视工具 JVM Statistics Monitoring Tool jstat [ option vmi ...

  9. 《深入理解JAVA虚拟机》----------第二章 JAVA内存区域与内存溢出异常,笔记(下)

    2. HotSpot虚拟机对象探秘 2.1 对象的创建 虚拟机遇到一条New指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初 ...

随机推荐

  1. JavaScript与Java数据类型的区别

    今天开始正式认真学习js,虽然在平常j2ee开发中也经常用到JS但并不精通,这次随笔记下js与Java数据类型的不同之处 Number 与java不同,js作为弱类型语言即使在浮点数与整数上也未作明确 ...

  2. MySQL Binlog解析(1)

    一.Binlog File Binlog files start with a Binlog File Header followed by a series of Binlog Event Binl ...

  3. MySQL二进制日志文件过期天数设置说明

    今天在处理业务库中二进制文件的时候,想更改二进制文件的过期天数,发现日期如果设置成2位以上的整数.都会出现如下的警告.不能成功的设置过期日期天数.MySQL版本从5.1到5.5都是一样的. mysql ...

  4. vim与shell终端操作技巧

    一.快速注释多行代码 1.添加//注释符 :10,50s#^#//#g       使用#作为分隔符 2.删除//注释符 :10,50s#^//##g       使用#作为分隔符 3.添加#注释符 ...

  5. postgre数据库插入错误:prepared statement “S_1”already exist, 解决办法

    在使用kettle工具(数据迁移软件)在postgre数据库中插入记录时,出现如下错误,解决办法: 在/etc/pgsql/pgbouncer.ini中修改配置,设置 server_reset_que ...

  6. NumPy副本和视图

    NumPy - 副本和视图 在执行函数时,其中一些返回输入数组的副本,而另一些返回视图. 当内容物理存储在另一个位置时,称为副本. 另一方面,如果提供了相同内存内容的不同视图,我们将其称为视图. 无复 ...

  7. HTTP Status 500 - javax.el.PropertyNotFoundException: Property 'lkmId' not found on type cn.itcast.entity.LinkMan

    报错 type Exception report message javax.el.PropertyNotFoundException: Property 'lkmId' not found on t ...

  8. 浅谈 session 会话的原理

    先谈 cookie 网络传输基于的Http协议,是无状态的协议,即每次连接断开后再去连接,服务器是无法判断此次连接的客户端是谁. 如果每次数据传输都需要进行连接和断开,那造成的开销是很巨大的. 为了解 ...

  9. 智课雅思词汇---二十五、形容词后缀-ate-fic-ose-ulent-olent-ous-ulous-y

    智课雅思词汇---二十五.形容词后缀-ate-fic-ose-ulent-olent-ous-ulous-y 一.总结 一句话总结: 1.形容词后缀-ate(determinate)? determi ...

  10. Python IOError: [Errno 22] invalid mode ('r') 解决方法

    环境 Anaconda3 Python 3.6, Window 64bit 书籍 O'Reilly出版的Wes McKinney编的<Python for Data Analysis> r ...