#
class文件格式
编译后被java虚拟机所执行的代码使用了一种平台无关的二进制格式来表示,以文件的形式存储,这种格式称为
class文件格式。
数据类型
java虚拟机可以操作的数据类型可以分为两类:
原始类型primitive type ,也叫做基本类型
引用类型reference type,
编译器应当在编译期间尽最大努力完成可能的类型检查,使得在运行期间无需进行这些操作,
虚拟机的字节码指令本身就可以确定它的指令操作数的类型是什么,可以利用这种特性直接确定操作数的数值类型。
java虚拟机是直接支持对象的,这里的对象可以是指动态分配的某个类的实例,也可以指某个数组。
原始类型与值
原始类型包括:
数值类型numeric type:
整数类型 integr type
byte:8位有符号二进制补码整数 -128~127 -2^7~2^7-1
short:16位 -2^15~2^15-1
int:32位 -2^31~2^31-1
long:64位 -2^63~2^63-1
char:16位无符号整数表示的、指向基本多文种平面的(Basic Multilingual Plane,BMP)的Unicode码点,以utf-16, 0~65535
浮点类型
float :单精度浮点数集合中的元素
double:双精度浮点数集合中的元素
boolean:
编译后都是用java虚拟机中的int数据类型来代替
returnAddress:
会被java虚拟机的jsr、ret和jsr_w指令使用
引用类型
引用类型:
类类型:class type
数组类型:array type
接口类型:interface type
运行时数据区
pc寄存器:
每一条java虚拟机线程都有自己的pc(program counter)寄存器
任意时刻,一条java虚拟机线程只会执行一个方法的代码,
如果不是native方法,寄存器保存java虚拟机正在执行的字节码指令的地址,
如果是native,pc寄存器的值是undefined
pc寄存器的容量至少应当能保存一个returnAddress类型的数据或平台相关的本地指针的值
java虚拟机栈
java virtual machine stack
每个vm线程都有属于自己私有的栈
这个栈与线程同时创建
用于存储栈帧 Frame
用于存储局部变量与一些尚未计算好的结果
栈帧可以在堆中分配
java虚拟机栈所使用的内存不需要保证是连续的
线程请求分配的栈容量超过vm允许的最大容量,就会出现StackOverflowError
动态扩展,内存不足时,就会出现OutOfMemoryError
java堆
heap是可供各个线程共享的运行时内存区域,
是所有类实例和数组对象分配内存的区域
堆在vm启动的时候就创建了
存储了垃圾回收器garbage collector垃圾收集器所管理的各种对象
方法区
method 可供各个线程共享的运行时内存区域
存储了每一个类的结构信息,
方法区在vm启动的时候创建的
运行时常量池
runtime constant pool
是class文件中每个类或接口的常量池表的运行时表示形式
每一个运行时常量池都在java虚拟机的方法区中分配
在加载类和接口到vm后,就创建对应的运行时常量池
本地方法栈
vm实现可能会用到传统的栈(c stack)来支持native方法(java以外的其他语言编写的方法)的执行,这个栈就是native method stack
本地方法栈实现可以是固定大小或者根据计算来动态扩展和收缩
如果采用固定大小的本地方法栈,每个线程可以在创建栈的时候独立选定
栈帧
用来存储数据和部分过程结果的数据结构
同时也用来处理动态链接 dynamic 、方法返回值和异常分派 dispatch exception
栈帧随着方法调用而创建,方法结束而销毁
栈帧的存储空间是由创建它的线程分配在vm栈中
每一个栈帧都有自己的本地变量表、操作数栈、和指向当前方法所属的类的运行时常量池的引用
本地变量表和操作数栈的容量是在编译期确定的
栈帧是线程本地私有的数据
不kennel在一个栈帧之中引用另外一个线程的栈帧
局部变量表
每个栈帧都包含一组称为局部变量表的变量列表
栈帧中局部变量表的长度有编译期决定
long和double的数据占用两个连续的局部变量
局部变量使用索引来进行定位访问
操作数栈
每个栈帧都包含一个称为操作数栈的后进先出栈,
栈帧中操作数栈的最大深度由编译期决定
一个long或者double类型的数据会占用两个单位的栈深度
动态链接
每个栈帧内部都包含一个指向当前方法所在类型的运行时常量池的引用,以便对当前方法的代码实现动态链接
一个方法若要调用其他方法,或访问成员变量,则需要通过符号引用来表示
动态链接的作用就是将这些以符号引用所表示的方法转换为对实际方法的直接引用
类加载的过程中将要解析尚未被解析的符号引用,并且将对变量的访问转化为变量在成都运行时,位于存储结构中的正确偏移量。
方法调用正常完成
方法调用异常完成
特殊方法
java中的构造器(jls 8.8)是一个名为的特殊实例初始化方法的形式出现的
一个类或者接口最多可以包含不超过一个类或接口的初始化方法,类或者接口就是通过这个方法完成初始化的
异常
vm里面的异常使用的是Throwable或其子类的实例来表示,
抛异常的本质实际上是程序控制权的一种即时的、非局部的转换——从异常抛出的地方转换至处理异常的地方
异常出现的原因:
athrow字节码指令被执行
vm同步检测到程序发生了非正常的执行情况,这时异常必将紧接着在发生非正常执行情况的字节码指令之后抛出,而不会在执行程序的过程中随时抛出
其他原因
调用了Thread或者ThreadGroup的stop方法
java虚拟机实现发生了内部错误
字节码指令集
vm的指令由一个字节长度的、代表着某种特定操作含义的操作码opcode以及跟随其后的零至多个代表此操作所需参数的操作数operand所构成
虚拟机中许多指令并不包含操作数,只有一个操作码
每个操作码只能由一个字节长度,所以直接限制了整个指令集的最大数量
字节码无法超过256种的限制就来源于此
操作码助记符
i代表int类型的数据操作
l long
s short
b byte
c char
f float
d double
a reference





类型转换指令
可以在两种java虚拟机数值类型之间相互转换
宽化类型转换是不会因为超过目标类型最大值而丢失信息的, 小转大
窄化类型转换,可能会丢失精度
对象的创建与操作
操作数栈管理指令
控制转移指令
字节码
CA FE BA BE /* u4 magic */
00 00 /* u2 minor_version=0 */
00 34 /* u2 major_version=52 */
/* java 1.8 */
指令集
/* L588 */
0 aload_0;
1 aload_1;
2 invokeinterface 1 2; /* java.lang.Object get(java.lang.Object arg0) */
7 dup;
8 astore_3;
9 ifnonnull 13;
12 aload_0;