java虚拟机规范(se8)——class文件格式(二)

4.4 常量池

  java虚拟机指令并不依赖类、接口、类实例或者数组的运行时布局。相反,指令依靠常量池中的符号信息。

  所有的常量池条目都有如下的通用结构:

cp_info {
    u1 tag;
    u1 info[];
}

  常量池表中的每一个项目是以1比特的标识位开始,指示是哪种cp_info条目。info数组的内容由标志位来决定。有效的标识以及对应的值见表4.4-A。每个标识位后面必须跟2个或更多字节,这些字节给出了这些指定常量的信息。额外信息的格式由标识值来决定。

  表4.4-A 常量池标识

Constant Type Value
CONSTANT_Class 7
CONSTANT_Fieldref 9
CONSTANT_Methodref 10
CONSTANT_InterfaceMethodref 11
CONSTANT_String 8
CONSTANT_Integer 3
CONSTANT_Float 4
CONSTANT_Long 5
CONSTANT_Double 6
CONSTANT_NameAndType 12
CONSTANT_Utf8 1
CONSTANT_MethodHandle 15
CONSTANT_MethodType 16
CONSTANT_InvokeDynamic 18

4.4.1 CONSTANT_Class_info结构

  CONSTANT_Class_info结构用于表示类或者接口:

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

  CONSTANT_Class_info结构的项目如下:

  tag

    标志位的值为CONSTANT_Class(7)。

  name_index

    name_index的值必须是常量池表中的一个有效索引。常量池在这个索引位置的条目必须是一个CONSTANT_Utf8_info结构,表示一个有效的二进制类或者接口名称,这个名称使用内部形式进行编码。

  因为数组是对象,字节码anewarray和multianewarray(不包括字节码new)能够通过常量池中的CONSTANT_Class_info结构来引用一个数组“类”。对于这样的数组“类”,类的名称就是数组类型来的描述符(4.3.2)。

  例如,二维数组类型int[][]的类名表示为[[I,然而Thread[]的类名表示为[Ljava/lang/Thread。

  只有数组的维度为255或者更少时,数组类型描述符才是有效的

4.4.2 CONSTANT_Fieldred_info,CONSTANT_Methodref_info,以及CONTANT_InterfaceMethodref_info结构

  字段,方法和接口方法使用相似的结构来表示:

  

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;
}

  这个结构的条目如下:

  tag

    CONTANT_Fieldref_info的tag值为CONSTANT_Fieldref(9)。

    CONTANT_Methodref_info的tag值为CONSTANT_Methodref(10)。

    CONTANT_InterfaceMethodref_info的tag值为CONSTANT_InterfaceMethodref(11)。

  class_index

    class_index的值必须是constant_pool表中的一个有效索引。索引位置的条目必须是一个CONSTANT_Class_info结构,表示一个有这个字段或者方法作为成员的类或者接口类型。

    CONSTANT_Methodref_info结构的class_index必须是一个类类型,不能是接口类型

    CONSTANT_InterfaceMethodref_info的class_index表示的条目必须是一个接口类型

    CONSTANT_Fieldref_info结构的class_index条目可以是类类型或者接口类型。

  name_and_type_index

    name_and_type_index的值必须是constant_pool表中的一个有效索引。索引位置的条目必须是一个CONSTANT_NameAndType_info结构,指示这个字段或者方法的名称和描述符。

  在CONSTANT_Fieldref_info中,指示的描述符必须是一个字段描述符。其它的必须是方法描述符。

  如果CONSTANT_Methodref_info结构的方法的名称以‘<‘ (‘\u003c‘)开始,那么这个名称必须是特殊名称<init>表示一个实例的初始化方法。它的返回类型必须是void。

4.4.3 CONSTANT_String_info 结构

  CONSTANT_String_info结构用来表示String类型的常量对象

CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}

  CONSTANT_String_info结构的项目如下:

  tag

    CONSTANT_String_info的tag值为CONSTANT_String(8)。

  string_index

    class_index的值必须是constant_pool表中的一个有效索引。索引位置的条目必须是一个CONSTANT_Utf8_info结构,表示需要初始化的String对象的Unicode代码点的序列。

4.4.4 CONSTANT_Integer_info和CONSTANT_Float_info结构

  CONSTANT_Integer_info和CONSTANT_Float_info结构表示4字节数值常量(int 和 float)。

CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}

CONSTANT_Float_info {
    u1 tag;
    u4 bytes;
}

  这些架构的项目如下:

  tag

    CONSTANT_Integer_info结构的tag值为CONSTANT_Integer(3).

    CONSTANT_Float_info结构的tag值为CONSTANT_Float(4).

  bytes

    CONSTANT_Integer_info的bytes项目表示int常量的值,int值的字节存储为大端模式。

    CONSTANT_Float_info的bytes项目表示float常量的值,使用IEEE 754单精度浮点格式。单精度格式的字节存储为大端模式。

    CONSTANT_Float_info结构表示的值由下面这些规则决定。值的字节首先按照int形式转换,然后:

    1、如果转换的比特位为0x7f800000,那么float的值为正无穷大

    2、如果转换的比特位为0xff800000,那么float的值为负无穷大

    3、如果转换的比特位在0x7f800001到0x7fffffff或者在0xff800001到0xffffffff之间,那么float的值为NaN。

    4、其它情况下,flaot的值根据比特位计算如下:

    

int s = ((bits >> 31) == 0) ? 1 : -1;
int e = ((bits >> 23) & 0xff);
int m = (e == 0) ?
          (bits & 0x7fffff) << 1 :
          (bits & 0x7fffff) | 0x800000;

    那么float的值等于数学表达式s · m · 2e-150.的结果

4.4.6 CONSTANT_NameAndType_info结构

  CONSTANT_NameAndType_info结构用来表示字段和方法,但是不指明属于哪个类或者接口类型:

CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}

  CONSTANT_NameAndType_info结构的项目如下:

  tag

    CONSTANT_NameAndType_info结构的tag项目的值为CONSTANT_NameAndType(12).

  name_index

    name_index的值必须是常量池表中的有效索引。这个索引位置必须是一个CONSTANT_Utf8_info结构,表示一个特殊的方法名<init>或者一个有效的非限定名称指示一个字段或者方法。

  desciptor_index

    name_index的值必须是常量池表中的有效索引。这个索引位置必须是一个CONSTANT_Utf8_info结构,表示一个有效的字段描述符或者方法描述符。

4.4.7 CONSTANT_Utf8_info结构

  CONSTANT_Utf8_info结构用来表示一个字符串常量值:

  

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

  结构中的项目如下:

  tag

    CONSTANT_Utf8_info的tag值为CONSTANT_Utf8(1).

  length

    length的值bytes数组中字节的数量(不是string的长度)。

  bytes[]

    bytes数组包含的字符串的字节。

    字节的值不能为(byte)0

    字节的值不能为(byte)0xf0 到 (byte)0xff.

  字符串的内容使用修正的UTF-8编码。使用修正的UTF-8编码以便每个代码点仅使用一个字节来表示仅包含非空ASCII字符的代码点序列,也可以表示Unicode代码空间的所有代码点。修正的UTF-8不以空值结束。编码过程如下:

  代码点在‘\u0001‘ 到 ‘\u007F‘范围间,使用一个单独的字节来表示:

  

0 bits 6-0

  7位的数值给出了代码点表示的值。

  null代码点(‘\u0000‘)和代码点在‘\u0080‘到‘\u07FF‘范围的使用一对字节x和y来表示:

  x:

1 1 0 bits 10-6

  y:

1 0 bits 5-0

  这两个字节的代码点的值为:

((x & 0x1f) << 6) + (y & 0x3f)

  代码点在‘\u0800‘到 ‘\uFFFF‘范围的使用三个字节x,y,z来表示:

  x:

1 1 1 0 bits 15-12

  y:

1 0 bits 11-6

  z:

1 0 bits 5-0

  三字节表示的代码点的值为:

  

((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)

  代码点高于U + FFFF的字符(所谓的补充字符)通过分别编码其UTF-16表示的两个代理代码单元来表示。每个代理代码单元由三个字节表示。这意味着补充字符由六个字节u,v,w,x,y和z表示:

u:
1 1 1 0 1 1 0 1
v:

1 0 1 0 (bits 20-16)-1

w:
1 0 bits 15-10
x:
1 1 1 0 1 1 0 1
y:
1 0 1 1 bits 9-6
z:
1 0 bits 5-0

  六字节表示的代码点的值为:

0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) +
((y & 0x0f) << 6) + (z & 0x3f)

  多字节字符的字节以大端模式(高字节优先)顺序存储在类文件中。

  这种格式与“标准”UTF-8格式有两点不同。首先,使用2字节格式而不是1字节格式对空字符(char)0进行编码,以便修正的UTF-8字符串永远不会嵌入空值。其次,仅使用标准UTF-8的1字节,2字节和3字节格式。 Java虚拟机无法识别标准UTF-8的四字节格式;它使用自己的2*三字节格式。

原文地址:https://www.cnblogs.com/lilinwei340/p/11408611.html

时间: 2024-10-21 00:36:47

java虚拟机规范(se8)——class文件格式(二)的相关文章

java虚拟机规范(se8)——class文件格式(六)

4.7.4 StackMapTable 属性 StackMapTable 属性是一个变长属性,位于 Code(§4.7.3)属性的属性表中.这个属性会在虚拟机类加载的类型阶段(§4.10.1)被使用. StackMapTable 属性包含 0 至多个栈映射帧(Stack Map Frames),每个栈映射帧都显式或隐式地指定了一个字节码偏移量,用于表示局部变量表和操作数栈的验证类型(Verification Types §4.10.1). 类型检测器(Type Checker)会检查和处理目标方

java虚拟机规范阅读(四)Java虚拟机指令集简介

Java 虚拟机的指令由一个字节长度的.代表着某种特定操作含义的操作码(Opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(Operands)所构成.虚拟机中许多指令并不包含操作数,只有一个操作码. 如果忽略异常处理,那 Java 虚拟机的解释器使用下面这个伪代码的循环即可有效地工作: do {   自动计算 PC 寄存器以及从 PC 寄存器的位置取出操作码;   if (存在操作数) 取出操作数;   执行操作码所定义的操作 } while (处理下一次循环); do { 自动计算

java虚拟机规范阅读(一)简介

java虚拟机规范在日常工作中可以说根本用不到,但作为一个完美主义者,感觉如果进入java这个行业,对它的方方面面不去掌握的话,未免有些遗憾,我没有那些改写java语言大师们的天赋,我只能站在他们的肩膀,来掌握他们创造的技术. 闲话不多说,我会认真读java虚拟机并写下自己理解或者有用的东西,看到的.写下的.说出的才是学到的,读者可以去http://down.51cto.com/自行下载java虚拟机规范. 关于java虚拟机规范: 本规范描述的是一种抽象化的虚拟机的行为,而不是任何一种(译者注

《Java虚拟机规范》阅读笔记-数据类型

<Java虚拟机规范>阅读笔记-数据类型 JVM 数据类型 1.概述 Java虚拟机的数据类型可分为两大类:原始类型(Primitive Types,也称为基本类型)和引用类型(Reference Types). Java虚拟机用不同的字节码指令来操作不同的数据类型 . 2.原始类型 原始类型是最基本的元素,用于构成复杂的引用类型.与世间万物一样,都是由最基本的化学元素组合而成. 原始类型又分为三类:数值类型(Numberic Types).布尔类型(Boolean Type).ReturnA

java虚拟机规范阅读(三)异常

Java虚拟机里面的异常使用Throwable或其子类的实例来表示,抛异常的本质实际上是程序控制权的一种即时的.非局部(Nonlocal)的转换--从异常抛出的地方转换至处理异常的地方. 绝大多数的异常的产生都是由于当前线程执行的某个操作所导致的,这种可以称为是同步的异常.与之相对的,异步异常是指在程序的其他任意地方进行的动作而导致的异常.Java虚拟机中异常的出现总是由下面三种原因之一导致的: 1.虚拟机同步检测到程序发生了非正常的执行情况,这时异常将会紧接着在发生非正常执行情况的字节码指令之

Java虚拟机规范阅读(二)IEEE754简介以及Java虚拟机中的浮点算法

什么是浮点数 在计算机系统的发展过程中,曾经提出过多种方法表达实数.典型的比如相对于浮点数的定点数(Fixed Point Number).在这种表达方式中,小数点固定的位于实数所有数字中间的某个位置.货币的表达就可以使用这种方式,比如 99.00 或者 00.99 可以用于表达具有四位精度(Precision),小数点后有两位的货币值.由于小数点位置固定,所以可以直接用四位数值来表达相应的数值.SQL 中的 NUMBER 数据类型就是利用定点数来定义的.还有一种提议的表达方式为有理数表达方式,

Java虚拟机工作原理详解 ( 二 )

首先这里澄清两个概念:JVM实例和JVM执行引擎实例,JVM实例对应了一个独立运行的Java程序,而JVM执行引擎实例则对应了属于用户运行程序的线程:也就是JVM实例是进程级别,而执行引擎是线程级别的. JVM是什么?—JVM的生命周期 JVM实例的诞生:当启动一个Java程序时,一个JVM实例就产生了,任何一个拥有 publicstaticvoidmain(String[]args)函数的class都可以作为JVM实例运行的起点,既然如此,那么JVM如何知道 是运行classA的main而不是

《Java虚拟机规范》阅读笔记-运行时数据区

Java虚拟机运行时数据区包括PC寄存器.Java虚拟机栈.Java堆.方法区.本地方法栈.运行时常量池六个部分. 1. PC寄存器 PC寄存器(又叫程序计数器,Program Counter Register),每一条Java虚拟机线程都有自己的PC寄存器.PC寄存器报错当前正在执行方法的字节码指令地址:如果当前方法是native的,则PC寄存器的值为undefined. 2. Java虚拟机栈 Java虚拟机栈(Java Virtual Machine Stack),每一条Java虚拟机线程

《深入理解Java虚拟机》笔记 第十二章 内存模型

主内存与工作内存 ? Java内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量值这样的底层细节. ? ? 此处的变量(Variable)与Java编译中所说的变量略有区别,它包括了实例字段,静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,因为后者是线程私有的,不会被共享,自然就不存在竞争的问题. ? ? 为了获得比较好的执行效率,Java内存模型并没有限制执行引擎使用处理器的特定寄存器或缓存来和主内存进行交互,也没有限制即时编译器调整代