第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. Mac 一键显示所有隐藏文件 不要那么六好吧

    系统应简洁而有效,对一般用户来说这一点尤为重要.不必要让普通用户知道的信息往往会给他们造成困扰,因而,隐藏掉他们便是个不错的选择,既可以保证系统平稳流畅运行,也可以为用户提供友好界面. 对于开发者而言 ...

  2. Mac & Xcode 技巧

    1.Xcode 删除过期的Provisioning Profile文件 解决:Xcode 中所有的Provisioning Profile文件路径,都在 ~/Library/MobileDevice/ ...

  3. sqlmap的简要使用方法

    仅作为备忘!!! sqlmap使用: 注入类型有四种分别为:boolean-based blind.error-based.stacked queries.inline query. sqlmap - ...

  4. git多站点帐号配置

    事件场景 小明同学是一个非常努力的coder,业余时间都花在了github.com上面,因为公司的台式机性能比较好,小明同学想在公司电脑上面也进行开发.但是github上面的帐号邮箱跟公司分配的帐号邮 ...

  5. 第十节课-RNN介绍

    2017-08-21 这次的课程介绍了RNN的相关知识: 首先是RNN的几种模型: 分别又不同的应用场景,包括机器翻译,视频的分类... RNN的解释: 主要的特点就是用到了上一个隐含状态的信息,所以 ...

  6. maven更换下载镜像源-解决下载慢问题

    Maven是当前流行的项目管理工具,但官方的库在国外经常连不上,连上也下载速度很慢.国内oschina的maven服务器很早之前就关了.今天发现阿里云的一个中央仓库,亲测可用. 1 <mirro ...

  7. Python基础笔记系列六:字典

    本系列教程供个人学习笔记使用,如果您要浏览可能需要其它编程语言基础(如C语言),why?因为我写得烂啊,只有我自己看得懂!! 字典字典的元素是由一对对键值对组成,每一对之间用逗号隔开,将所有的键值对用 ...

  8. Largest Rectangle in Histogram, 求矩形图中最大的长方形面积

    问题描述: Given n non-negative integers representing the histogram's bar height where the width of each ...

  9. ZC_疑问

    1. 应该可以将所有的 jni需要的函数都放在一个 dll中(Windows下),然后 多个java项目就只要调用一个dll了. 可以测试一下 2.

  10. python中sorted()函数的用法

    一. 定义 sorted()函数对所有可迭代的对象进行排序操作 二. 语法 sorted(iterable [, key[, reverse]]]) iterable:可迭代对象 key:主要是用来进 ...