引言

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虚拟机层面上类实例的构造方法名为,在实例的初始化通过invokespecial指令调用。类和接口的初始化通过,在类加载时由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)

java returnaddress_Java虚拟机规范】Java SE 7虚拟机结构相关推荐

  1. Java虚拟机规范 Java SE 8版 - class文件格式(二)

    Java虚拟机规范 Java SE 8版 - class文件格式(二) 4.5 字段 4.6 方法 4.7 属性 4.7.1 自定义和命名新的属性 4.7.2 ConstantValue 属性 4.7 ...

  2. Java虚拟机规范 Java SE 8版 - class文件格式(一)

    Java虚拟机规范 Java SE 8版 - class文件格式(一) 4.1 ClassFile 结构 4.2 各种名称的内部表示形式 4.2.1 类和接口的二进制名称 4.2.2 非限定名 4.3 ...

  3. java 虚拟机规范_Java虚拟机规范----Java虚拟机结构

    Java体系和一些基本概念 Java平台的结构图: JVM与JRE.JDK关系? JVM:Java Virtual Machine(Java虚拟机),负责执行符合规范的Class文件 JRE: Jav ...

  4. Java语言编码规范(Java Code Conventions)

    1 介绍(Introduction) 1.1 为什么要有编码规范(Why Have Code Conventions) 编码规范对于程序员而言尤为重要,有以下几个原因: - 一个软件的生命周期中,80 ...

  5. java标识符命名规范 java关键字 标识符

    前言 标识符的认识和熟练运用对程序员来说很重要,标识符是程序员为程序组件起的名字.起名字是一门艺术,这一点对标识符也一 样.一个好的标识符命名风格和习惯,能够很大程度上增加代码的可读性. . Java ...

  6. Java虚拟机规范(目录)

    Java虚拟机规范 Java SE 11 Edition 介绍 Java虚拟机介绍 Java虚拟机的结构 class文件格式 数据类型 原始类型和值 引用类型和值 运行时数据区 帧 对象的表示 浮点算 ...

  7. 深入理解java虚拟机之java内存区域

    java虚拟机在执行java程序的时候会把它所管理的内存分为多个不同的区域,每个区域都有不同的作用,以及由各自的生命周期,有些随着虚拟机进行的启动而存在,有些区域则依赖于用户线程的启动或结束而建立或销 ...

  8. 区分 JVM 内存结构、 Java 内存模型 以及 Java 对象模型 三个概念

    本文由 简悦 SimpRead 转码, 原文地址 https://www.toutiao.com/i6732361325244056072/ 作者:Hollis 来源:公众号Hollis Java 作 ...

  9. 【转】JVM内存结构 VS Java内存模型 VS Java对象模型

    JVM内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途. 其中有些区域随着虚拟机进程的启动而 ...

  10. java对象模型是什么_蓝石榴_个人博客_JVM内存结构、Java内存模型、Java对象模型...

    JVM内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途.其中有些区域随着虚拟机进程的启动而存 ...

最新文章

  1. 从计算机移到u盘如何加快速度,我的电脑移动文件到U盘里时进度很慢很慢,怎么让它变快?...
  2. IDEA中的项目没有被SVN管理解决办法
  3. 基因分子生物学~tRNA,mRNA,蛋白质合成
  4. 【无码专区2】序列划分(数学)
  5. 什么是openstack_您在OpenStack Summit 2016上错过了什么
  6. javaone_JavaOne演讲者选择了您不容错过的10个会话
  7. CortexM0开发 —— UART时序分析
  8. 关于windows下的libtorch配置
  9. mui.ajax php,求助!!!关于mui ajax获取不到后台数据
  10. 产品工作中/阅读中的涓滴意念
  11. 风云的银光志Silverlight4.0教程之遍历访问客户端用户的本地文件
  12. vue.js开发环境部署
  13. 小写的tensor接受数据,大写的Tensor()接受的是shape,数据的维度
  14. 400款营销策划PPT模板免费下载
  15. R如何读取txt文件
  16. Windows系统中禁止某应用程序联网操作方法
  17. 集体备课模板_集体备课活动记录
  18. PDF417美国驾照条形码信息自动识别
  19. nim博奕和巴什博奕
  20. 软考 案例分析__预测

热门文章

  1. javaScript中常见的几种报错类型
  2. MySQL 主从架构配置详解
  3. String类中的equals方法与Object类中的equals方法的不同点
  4. OpenCV的cv::cvtColor函数之“CV_RGB2GRAY”: 未声明的标识符错误解决方法
  5. 15 分钟带你入门 sklearn 与机器学习(分类算法篇)
  6. linux修改mysql默认大小写配置,linux下设置mysql不区分大小写
  7. php 后台进程,php是否适合做后台长驻程序
  8. 为什么每次关机都要关闭mysql_在机器关机时关闭mysql服务实例
  9. java8 stream中的惰性求值
  10. POST与GET两种请求方式的区别: