《Java虚拟机规范》(Java SE 7)笔记-----虚拟机结构

引言

Java虚拟机可以看做是一台抽象的计算机,如同真实的计算机那样,有自己的指令集以及各种运行时内存区域。Java虚拟机与Java语言没有必然联系,它只与特定的二进制文件即Class文件关联,Class文件包含了Java虚拟机指令(字节码),符号表以及一些需要的辅助信息。任何一种语言只要可以被编译成有效的Class文件,都可以在Java虚拟机上面运行。

1.数据类型

Java虚拟机可以操作的数据类型分为两类,原始类型(Primitive Types)与引用类型(Reference Types)。原始类型不需要额外的手段来确定运行期他们实际的数据类型,指令本身就可以确定;引用类型编译器应当在编译期间尽最大努力完成类型检查。

2.原始类型与值

原始类型包括数值类型(Numberic Types)、布尔类型(Boolean Type)和returnAddress类型。

数值类型:整数类型byte short int long char,浮点类型float double,与IEEE 754格式取值和操作一致。

布尔类型:Java虚拟机定义了boolean这种数据类型,但是没有指令支持,涉及到boolean值类型的运算,都会被编译成int类型来代替。

returnAddress类型:被指令jsr,jsr_w,ret使用,从JDK7开始虚拟机不允许出现这几条指令,所以不用过于关注。returnAddress类型的值指向一条虚拟机指令的操作码,初衷是用来实现Java语言中的finally语句块。jsr与ret是一起使用的,jsr跳转到指定的offet位置,并将jsr下一条指令压入栈顶,就是retureAddress类型了,使用ret返回到指定的指令位置。参考:)

3.引用类型与值

类类型(Class Types) 数组类型(Array Types) 接口类型(Interface Types),分别对应类实例,数组实例,实现某个接口的实例。

引用类型值有一个特殊的值null,当一个引用不指向任何对象时,它的值用null表示,可以转换为任意类型,Java虚拟机没有规定null的实现应用用怎样的编码。

4.运行时数据区

运行时数据区

PC(Program Counter)寄存器:Java虚拟机中每一条线程都有自己的PC寄存器,用来保存当前方法的指令地址(也就是returenAddress类型的值),如果方法是native的,则保存本地指针的值。

Java虚拟机栈(Java Virtual Machine Stack):Java虚拟机每一条线程都有私有的栈,用来存储局部变量与一些过程结果的地方,由栈幀(Frames)组成。Java虚拟机栈能够被实现成固定大小或者动态扩展模型。异常情形:(1)如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量,则抛出StackOverflowError异常 (2)如果Java虚拟机栈能够动态扩展,申请不到内存去创建新的栈,则抛出OutofMemoryError异常。

**Java堆(Heap) **:堆区是线程共享的区域,用分配类实例,数组对象的内存区域。Java虚拟机启动的时候就会被创建,并且分配的内存由GC(Garbage Collector)管理,这些对象无需也无法显示地被销毁。当创建的堆超过了GC能够提供的容量,则会抛OutofMemoryError异常。

方法区(Method Aera):Java虚拟机启动时创建,线程共享的内存区域,编译代码(类信息,类方法,成员变量,运行时常量等信息)的存储区域。虽然方法区是堆区的逻辑组成部分,虚拟机实现可以选择是否回收该区域垃圾。方法区内存空间不满足内存分配要求,同样抛出OutOfMemoryError。

运行时常量池(Rumtime Constant Pool): 类似符号表,从编译时可以知道的字面量到必须运行是解析后才能知道的方法或者字段引用,位于方法区中,在类和接口被创建,对应的运行时常量池就被创建。

本地方法栈(Native Method Stack): 用来支持native方法的执行,在线程创建时分配。和虚拟机栈类似,能够动态扩展,栈容量超过本地方法栈允许最大容量抛StackOverflowError,无法申请到足够的内存去扩展抛OutOfMemoryError。

4.栈幀(Frame)

用来存储数据或部分过程结果的数据结构,处理动态链接(Dynamic Linking)、方法返回值、异常分派(Dispatch Exception)。随着方法调用创建,方法结束销毁。每一个栈幀都有自己的局部变量表(Local Variables),操作数栈(Operand Stack)和指向当前方法所属的类的运行时常量池的引用,并且容量是在编译期确定的。

栈幀是线程本地私有数据,不可能在一个栈幀之中访问另一条线程的栈幀。

局部变量表:局部变量表可以保存前面所述的虚拟机的数据类型,其中两个局部变量保存一个类型为long或double的数据。局部变量表使用索引来访问,可以想象为一个数组的模型,当方法调用时,它的参数从零开始连续的存放在局部变量表示,如果是实例方法,则第0个局部变量一定是调用方法对象的引用(即Java里的this)。

操作数栈:后进先出(Last-In-First-Out,LIFO)栈,用来存放Java虚拟的指令执行时操作数以及执行后的结果,操作数栈与局部变量表可以相互转移。在方法调用的时候,操作数栈用来准备调用方法的参数以及接收方法返回结果。

每一个栈幀内部都包含一个指向运行时常量池的引用来支持当前方法的代码实现动态链接,方法调用或者访问成员变量时是通过符号引用表示,动态链接的作用就是将符号引用转化为实际的方法引用。

5.浮点算法

Java虚拟机中浮点操作在遇到非法操作(如被零整除,上限溢出等)不会抛exception。

6.初始化方法

Java虚拟机层面上类实例的构造方法名为<init>,在实例的初始化通过invokespecial指令调用。类和接口的初始化通过<clinit>,在类加载时由Java虚拟机自身隐式调用,没有任何指令可以调用这个方法。

7.异常

异常的本质是程序控制权的一种及时、非局部的转换(从异常抛出的地方转至处理异常的地方)。当前前程抛出的异常称为同步异常,非当前线程抛出的异常为异步异常。虚拟机异常的情形有:

  • 指令非正常执行,如数组越界,栈溢出等
  • athrow指令被执行
  • 虚拟机内部错误或者Thread/ThreadGroup的stop方法被执行(异步异常)

Java虚拟机执行每一个方法都会配有零至多个异常处理器(Exception Handlers),每个方法的异常处理器都存储在一个表中,在运行时出现异常后,会按照异常处理器的描述执行。

8.字节码指令集简介

Java虚拟机的指令有一个字节长度的操作码(Opcode)和操作数(Operands)组成,由于操作码为一个字节,所以虚拟机的字节码指令最后有256条。

Java虚拟机解释器伪代码:

do {
    自动计算PC寄存器以及从PC寄存器的位置取出操作码
    if(存在操作数)取出操作数
    执行操作码所定义的操作
}while(处理下一次循环);

由于Java虚拟机字节码数量限制,对于特定类型操作只提供了有限的类型相关指令去操作它。多数对于boolean byte short char类的数据操作,实际上都是使用相应对int类型作为运算类型。

加载存储指令:用于局部变量表与操作数栈之间来回传输,例如:
istore_1 指令作用是从操作数栈中弹出一个int型的值,并保存在第一个局部变量中
iload_1 指令作用是将第一个局部变量的值压入操作数栈

运算指令:用于两个操作数栈上的值进行运算,并把结果重新存入操作数栈栈顶。例如iadd isub,Java虚拟机没有明确规定整型数据溢出情况,但规定除法指令(idiv/ldiv),求余指令(irem和lrem)的除数为零时抛ArithmeitcException异常。

类型转换指令:Java虚拟机直接支持宽化类型转换(Widening Numberic Conversions),如int类型到long float double类型,long 到float double类型。 窄化类型转换(Narrowing Numberic Conversions)会导致符号位丢失,精度丢失,指令有i2b i2c f2i f2l等等。

对象创建与操作:类实例与数组都是对象,使用不同的指令操作。
创建对象new,创建数组newarray anewarray multinewarray。
访问字段getfield putfield getstatic putstatic
加载数组元素到操作数栈:iaload aaload等
将操作数栈的值存储到数组元素:iastore aastore等
取数组长度的指令arraylength
检查类实例类型的指令instanceof checkcast

操作数栈管理:pop pop2 dup dup2 swap等

控制转移指令:
条件分支:ifeq iflt...
复合条件分支:tableswitch lookupswitch
无条件:goto goto_w jsr ret..

方法调用和返回指令:四条指令用于方法调用,
invokevirtial 调用实例方法
invokeinterface 调用接口方法
invokespecial 调用特殊的实例方法,例如实例初始化方法,私有方法以及父类方法
invodestatic 调用静态方法
方法返回指令 ireturn(同样,boolean byte char short int类型时时候) areturn return(返回类型类void)

抛出异常:显式抛出的指令athrow,Java虚拟机检测到指令执行异常由Java虚拟机自动抛出。

同步:方法级同步时隐式的,常量池的方法列表里面指令。指令序列同步的关键字 monitorennter monitorexit,对应Java中sychronized的代码块。

8.类库

Java虚拟机必须对不同平台下的Java类库提供充分的实现,某些与操作系统密切相关的类库需要Java虚拟机的本地方法来实现:

  • 反射 java.lang.relect包与java.lang.Class类
  • 类和接口的加载与创建 java.lang.ClassLoader类
  • 安全相关 java.lang.SecurityManager
  • 多线程
  • 弱引用

9.公有设计,私有实现

虚拟机实现必须能够读取Class文件,并且精确实现虚拟机代码的含义,怎么实现是实现者自己的事情,只要外部接口看起来与规范描述的一样。目前虚拟机实现方式主要有两种:

  • 将输入的Java虚拟代码在加载的时或执行时翻译成另外一种虚拟机指令集。(如Davilk)
  • 将输入的Java虚拟机代码在加载时或执行时翻译成宿主机CPU的本地指令集(如ART)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容

  • 写在前面:因为之前读过周志明的《深入理解Java虚拟机》,并且也一直在阅读相关的博客,所以对Java虚拟机的知识有...
    EakonZhao阅读 12,647评论 6 32
  • JAVA虚拟机的生命周期 一个运行时的Java虚拟机实例的天职是:负责运行一个java程序。当启动一个Java程序...
    Solang阅读 1,148评论 0 19
  • Java 虚拟机屏蔽了与具体操作系统平台相关的信息,使得 Java 语言编译程序只需生成在 Java 虚拟机上运行...
    寻梦的尕柳阅读 847评论 0 11
  • 如何解决曲线行驶老压线问题?作者:东方时尚驾校 来源:东方时尚驾校 发布时间:2017-03-27 16:37...
    iOS的Developer阅读 1,231评论 0 3
  • 2016-01-29,我们结束了第三年的复训,开始了我新一年的乘务员生涯。 没错我是一个空姐 身高168 有些微...
    叫我二宝宝阅读 392评论 2 0