java虚拟机学习笔记

java虚拟机,可能是指如下三个概念:

1、java虚拟机抽象规范

2、java虚拟机抽象规范在某个平台上的具体实现

3、某个运行中的虚拟机实例


java虚拟机内部线程分为守护线程和非守护线程。守护线程由虚拟机自己使用,如垃圾收集线程。而java程序的初始线程(始于main函数),则是非守护线程。

当某个进程的所有非守护线程都终止时,该java虚拟机运行实例的生命周期结束,虚拟机实例也将退出。


java虚拟机规范通过子系统、内存区、数据类型及指令来描述java虚拟机实现的外部行为,并展示了java虚拟机内部体系结构的抽象描述(注意这种抽象描述并非强制,不要求虚拟机内部体系结构一定要按照这种描述来实现,只是为了定义其外部行为)。

上图简要的描述了java虚拟机的内部体系结构(貌似忽略了某些组件如垃圾收集子系统?)。


java虚拟机规范定义的数据类型

java虚拟机的机器字长通常根据底层主机平台的指针长度来选择,运行时数据区的大部分内容都是按机器字来定义的。

然而机器字的长度不影响虚拟机的外部行为,对应用程序是透明的。


类装载子系统

类装载子系统负责查找和装载class,分为启动类装载器和用户自定义类装载器。不同装载器加载的类分属不同的命名空间。

装载、连接及初始化过程:

1、装载--查找并加载2进制数据

2、连接--执行验证、准备及解析(可选)

验证  确保被导入类型的正确性

准备  为类变量分配内存并初始化

解析  将类变量中的符号引用转换为直接引用(指针?)

3、初始化--将类变量初始化为正确值(执行构造函数?)


方法区

存储类型信息的内存在逻辑上被称为方法区。

方法区被所有线程共享,对方法区内存的操作必须是线程安全的。

方法区大小不必固定,甚至不必是连续的内存区域,方法区也可以被垃圾收集。


类型信息主要包括:

1、类型的全限定名

2、类型的直接父类的全限定名(object类例外)

3、是类类型还是接口类型

4、类型的访问控制修饰符

5、类型所实现的接口的有序列表

此外方法区还为每个被加载的类型存储

1、该类型的常量池

2、字段信息

3、方法信息

4、除常量以外的所有类静态变量

5、一个到该类所对应的类装载器的引用

6、一个到Class类的引用

常量池

类所使用的常量的有序集合,包括直接常量(string、integer和floating point常量),以及对其他类、字段、方法的符号引用。类似数组的方式通过索引访问常量池。

字段信息包括字段名、字段类型、修饰符。

方法信息包括方法名、返回类型、参数数量及类型(按声明顺序)、方法的修饰符,此外,如果方法非abstract也非native,那么还需要储存:字节码、操作数栈及栈帧的局部变量区大小、异常表。

另,类静态变量与常量(final修饰变量)的处理不同,他们虽然都储存在方法区,但变量是作为声明变量的类型的一部分数据所储存的,而常量则作为被任何使用该常量的类型的一部分数据所储存。

指向ClassLoader类实例的引用:虚拟机会在动态链接期间使用这个信息,如一个类型引用另一个类型,那么虚拟机会使用发起引用类型的类装载器去装载被引用的类型。

指向Class类实例的引用:每个被装载的类都会创建一个Class类的实例,并将该实例与方法区中存储的信息关联起来。通过这个Class对象的引用,就能通过Class类中的方法获得这个类的相关信息。那么运行程序就可以访问方法区中的类型信息。

方法表

方法表是一个数组,它的元素是所有它的实例可能调用的实例方法的直接引用,包括从父类继承的方法。运行时通过方法表快速搜寻在对象中调用的实例方法。


每个运行中的java虚拟机实例都存在一个堆空间,为所有线程共享。虚拟机有一条指令用于在堆中分配新对象。

垃圾收集

垃圾收集的主要工作是自动回收不再被运行中的程序所引用的对象所占用的内存。此外它可能会去移动仍在使用的对象以减少碎片。

对象的内部表示

通过对象引用,虚拟机必须能快速定位对象实例的数据(实例变量),此外还要能通过对象引用访问相应的类数据(方法区中的类型信息)。

数组

在java中,数组总是被表示为对象。多维数组被表示为数组的数组。


程序计数器

每个线程都有对应的PC(程序计数)寄存器,大小是一个字长。当程序运行java方法时,其值总是下一条虚拟机指令的地址。如果程序在执行本地方法,那么值为“undefined”。


java栈

java栈以帧为单位保存本线程的运行状态。java栈上的数据为线程私有,不可能被其他线程访问到,不用考虑同步问题。

栈帧

栈帧由三部分组成:局部变量区、操作数栈和帧数据区。局部变量区和操作数栈的大小在编译时确定并存入.class文件,栈数据区大小依赖于具体虚拟机的实现。

局部变量区

局部变量区被组织为以字长为单位,从0开始计数的数组。字节码指令通过从0开始的索引来使用其中的数据。

类实例方法中的局部变量区会隐含this引用,而类静态方法则不包含。

局部变量区只有对象引用,没有对象copy。

局部变量区的数据都是字对齐,byte、short、char在局部变量区都被当作int处理,只有存回堆或方法区中时才会被转换成原类型。long和double必须在局部变量区占据两项。

操作数栈

操作数栈也是以字长为单位的数组,与局部变量区不同的是,操作数栈不是通过索引来访问,而是通过压栈和退栈来访问的。

当java虚拟机不是通过寄存器而是通过操作数栈来获得执行指令所需的操作数时,那么我们认为该java虚拟机是基于栈的,反之则是基于寄存器的。

帧数据区

除了局部变量区和操作数栈外,java栈帧还需要一些数据来支持常量池解析、方法返回及异常派发。这些额外数据保存在帧数据区。

在程序运行最开始,常量池中对类、字段、方法的引用都是符号。只有当要使用该引用时,虚拟机才会进行解析。

当java方法返回时,虚拟机必须恢复发起调用的栈帧,设置pc寄存器所指向的指令;如果方法有返回值,还必须将返回值压入发起调用方法的操作数栈中。


本地方法栈

当调用本地方法时,java虚拟机会保持java栈不变,而只是简单的动态连接并调用本地方法。当本地方法回调java虚拟机中的java方法时,此时线程会保持本地方法栈的状态并进入到另一个java栈。

执行引擎决定字节码如何被执行

java虚拟机规范要求实现线程支持,一般使用本地线程来实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值