java虚拟机体系结构分析

1.Java虚拟机的生命周期
当启动一个Java程序时,一个虚拟机实例也就诞生了。当该程序关闭退出,这个虚拟机实例也就随之消亡。
Java虚拟机实例通过调用任意某个初始类的main()方法来运行一个Java程序。
只要还有任何非守护线程在运行,那么这个Java程序也在继续运行(虚拟机仍然存活)。
2.Java虚拟机的体系结构
 
每个Java虚拟机实例都有一个方法区以及一个堆,他们是由该虚拟机所有线程所共享。
当虚拟机装载一个.class文件时,它会从.class文件的二进制信息中解析类型信息填入方法区中,把创建的对象放到堆中。
当每一个新线程被创建时、它都将得到自己的PC寄存器〔程序汁数器)以及—个Java栈。
 
2.1 类装载器
装载 java 编译器编译好的字节码 *.class java api 的字节码到方法区。
java 有两种类装载器:
(1).
启动类装载器:系统唯一,属于虚拟机的一部分,用特定语言编写 ( 与虚拟机体层语言相通 ) 使用默认方式装载类,主要用来装载核心类库。
(2).
用户自定义类装载器:可有任意多个,用 java 编写,属于 java 应用程序的一部分,能被编译成字节码,并被虚拟机所装载。
一个装载器装载一个类及其该类所调用的一切类,使他们相互联系,并形成一个命名空间 (name space) ,每一个类装载器对应一个命名空间。即 java 中名字空间的原理。
类装载器子系统除了要定位和导人二进制 class 文件外,还必须负责验证被导入类的正确性,为类变量分配并初始化内存。以及帮助解析符号引用。
2.2 方法区
java 虚拟机中,关于被装载类型的信息存储在一个逻辑上被称为方法区的内存中。
方法区存放装载的类数据信息包括:
基本信息:
每个类的全限定名。
每个类的直接超类的全限定名 ( 可约束类型转换 )
该类是类还是接口。
该类型的访问修饰符。
直接超接口的全限定名的有序列表。
每个已装载类的详细信息:
·          运行时常量池:存放该类型所用的一切常量 ( 直接常量和对其他类型,字段,方法的符号引用 ) ,它们以数组形式通过索引被访问,是外部调用与类联系及类型对象化的桥梁。它是类文件 ( 字节码 ) 常量池的运行时表示。 ( 还有一种静态常量池,在字节码文件中 )
·          字段信息:类中声明的每一个字段的信息 ( 名,类型,修饰符 )
l         方法信息:类中声明的每一个方法的信息 ( 名,放回类型,参数类型,修饰符,方法的字节码和异常表 )
l         静态变量
·          到类 classloader 的引用:即到该类的类装载器的引用。
·          到类 class 的引用:虚拟机为每一个被装载的类型创建一个 class 实例,用来代表这个被装载的类。
·          方法表:虚拟机对每个装载的非抽象类,都生成一个方法表,把它作为类信息的一部分保存在方法区。方法表是一个数组,它的元素是所有它的实例可能被调用的实例方法的直接引用,包括那些从超类继承过来的实例方法。
执行过程
1)      搜索方法区,未果,读入 volcano.class 文件
2)      main 方法入口,执行第一条字节码,内容是为常量池第一项的类分配内存
3)      通过常量池指针找到第一项,发现是类型 lava 的全限定名
4)      搜索方法区,未找到 lava 类型信息,搜索并装载 lava.class 文件
5)      用装载后的方法区中 lava 类型数据的地址指针替换在 volcano 类型数据的常量表中的第一项。
6)      通过上步得到的指针从 lava 的类型信息中取得对象实例应分配内存大小,在堆中分配内存
7)      为对象分配内存后,把得到对象引用压入到 main() 方法的操作数栈中,完成第一条字节码指令
8)      PC 计数器指向下一条指令,通过操作数栈中的引用初始化 speed 变量为 5 ,同时初始化父类被继承字段
9)      全部初始化指令结束后,通过对象引用调用 flow() 方法。
2 3
Java程序在运行时创建的所有类实例或数组都放在同—个堆中。
每个Java程序都有它自己的堆空间——它们不会彼此干扰。
2.3.1垃圾收集:
2.3.2对象的内部表示:
只要有一个对象引用,虚拟机就必须能够快速地定位对象实例的数据。另外,它也必须能通过该对象引用访问相应的类数据(存储于方法区的类型信息),因此在对象中通常会有一个指向方法区的指针。
堆分配方案一:
堆分配方案二:
结合方法表后堆和方法区的实现方法:
上图中方法数据包括此方法的操作数栈和局部变量区的大小,此方法的字节码,异常表。
2.3.3 数组
和其他所有对象一样,数组也拥有一个与它们的类相关联的class实例,所有具有相同维度和类型的数组都是同一个类的实例,而不管数组的长度是多少。多维数组被表示成数组的数组,下图中[[I表示类型为Integer的两维数组,[I表示类型为Integer的一维数组。
 
2.4 Java栈
每当启动一个新线程,虚拟机就会为它建立一个java栈。java栈以方法帧为单位进行压入和弹出。
栈帧由三部分组成:局部变量区,操作数栈和帧数据区。前两者大小在编译时确定存放在.class文件中,帧数据区大小随虚拟机的实现不同而不同。
2.4.1局部变量区 
Java栈帧的局部变量区被组织为一个以字长为单位、从0开始计数的数组。int、float、reference和returnAddress值在数组中占据一项,byte、short、char转换成int,long和double占据连续两个字。
方法形参和局部变量在局部变量区分布入图,注意非静态和抽象方法的this引用放在0位置。boolean类型用int的0,1表示。
 
2.4.2操作数栈
操作数栈也是一个数组,但却是通过栈操作来访问。所谓操作数是那些被指令操作的数据。当需要对参数操作时如a=b+c,就将即将被操作的参数压栈,如将b和c压栈,然后由操作指令将他们弹出,并执行操作,此处由iadd指令将b和c弹出并相加,然后压入操作数栈(一系列均由iadd执行)然后由i_storex指令将结果弹出,存到索引x指向的局部变量区数组内(此处索引x指向局部变量a)。虚拟机将操作数栈作为工作区。
四条字节码指令执行状态:
 
2.4.3数据区
除了局部变量区和操作数栈外.Javn栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些信息都保存在Java栈帧的帧数据区。
栈数据区包含一个指向该方法异常表的引用,异常表每一项包括每个catch子句对应try子句的字节码开始和结束位置,该catch抛出异常类在方法区中数据信息的引用以及catch子句中字节码语句的开始位置。
 
2.5 本地方法栈
与调用的本地方法的语言相关,如调用的是一个c语言方法则为一个c栈。本地方法可以回调java方法。若有java方法调用本地方法,虚拟机就运行这个本地方法。在虚拟机看来运行这个本地方法就是执行这个java方法,如果本地方法抛出异常,虚拟机就认为是这个java方法抛出异常。
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值