图一 Java虚拟机的内部体系结构
每个Java虚拟机实例都有一个方法区以及一个堆,它们是由该虚拟机实例中所有线程共享的。
当虚拟机装载一个class文件时,它会从这个class文件包含的二进制数据中解析类型信息,然后把这些类型信息放到方法区中;
当程序运行时,虚拟机会把所有该程序在运行时创建的对象都放到堆中;
当每一个新线程被创建时,它都将得到它自己的PC寄存器(程序计数器)以及一个Java栈,如果线程正在执行的是一个java方法(非本地方法),那么PC寄存器的值将总是指示下一条将被执行的指令,而它的Java栈则总是存储该线程中java方法调用的状态—包括它的局部变量,被调用时传进来的参数,它的返回值,以及运算的中间结果等等。而本地方法调用的状态,则是以某种依赖与具体实现的方式存储在本地方法栈中,也可能是在寄存器或者其他某些与特定实现相关的内存中。
Java栈由许多栈帧(stack frme)或者说帧(frame)组成,一个栈帧包含一个方法调用。当线程调用一个Java方法时,虚拟机压入一个新的栈帧到该线程的java栈中,当该方法返回时,这个栈帧被从Java栈中弹出并抛弃。
Java虚拟机没有寄存器,其指令集使用Java栈来存储中间数据。
数据类型
Java虚拟机是通过某些数据类型来执行计算的,数据类型及其运算都是由java虚拟机规范严格定义的。数据类型可以分为两种:基本类型和引用类型。基本类型的变量持有原始值,而引用类型的变量持有引用值。术语“引用值”指的是对某个对象的引用,而不是该对象本身,与此相对,原始值则是真正的原始数据。Java语言中的所有基本类型同样也都是java虚拟机中的基本类型,但是boolean有点特别,虽然虚拟机也把boolean看做基本类型,但是指令集对boolean只有很有限的支持。当编译器把java源码编译成字节码时,它会用int或byte来表示boolean。设计boolean值的操作会使用int,boolean数组是当作byte数组来访问的。但是在“堆”区中,它也可以被表示为位域。
类型
|
范围
|
byte
|
8bit,带符号,二进制补码,[-2∧7,2∧7-1]
|
short
|
16bit,带符号,二进制补码,[-2∧15,2∧15-1]
|
int
|
32bit,带符号,二进制补码,[-2∧31,2∧31-1]
|
long
|
64bit,带符号,二进制补码,[-2∧63,2∧63-1]
|
char
|
16bit,不带符号,Unicode字符,[0,2∧16-1]
|
float
|
32bit,IEEE 754标准单精度浮点数
|
double
|
64bit,IEEE 754标准双进度浮点数
|
returnAddress
|
同一方法中某操作码的地址
|
reference
|
堆中对某对象的引用,或者是null
|
Java虚拟机中,最基本的数据单元就是字(word),它的大小是由每个虚拟机实现的设计者来决定的。字长必须足够大,至少是一个字单元就足以持有byte、short、int、char、float、returnAddress、reference类型的值,而两个字单元就足以持有long或者double类型的值。因此,虚拟机实现的设计者至少得选择32位作为字长。
方法区
由于所有线程都共享方法区,因此它们对方法区数据的访问必须被设计为线程安全的。
类型信息 对每个装载的类型,虚拟机都会在方法区中存储以下类型信息:
l 这个类型的全限定名
l 这个类型的直接超类的全限定名
l 这个类型是类类型还是接口类型
l 这个类型的访问修饰符
l 任何直接超接口的全限定名的有序列表
除这些基本信息外,还需要如下信息:
l 该类型的常量池
l 字段信息
l 方法信息
l 除了常量以外的所有类变量
l 一个到类ClassLoader的引用
一个到Class类的引用