深入Java虚拟机读书笔记[5]
第五章 Java虚拟机
1. Java虚拟机内部体系结构
在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。
堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。
2. 数据类型
Java虚拟机中的数据类型分为基本类型和引用类型。Java语言中的所有基本类型同样为Java虚拟机中的基本类型。Java虚拟机把boolean看作基本类型,但是指令集对boolean的支持十分有限。编译为字节码的时候使用int或byte表示boolean。boolean数组当作byte数组使用,但是在堆区也可能被表示为位域。
3. 类装载子系统
顺序:装在、连接、初始化
Java虚拟机中有两种类装载器:启动类装载器和用户自定义类装载器。前者是Java虚拟机实现的一部分,后者则是Java程序的一部分。后者通往虚拟机的通道有defineClass、findSystemClass、resolveClass。
4. 方法区
方法去存储关于被装载类型的信息。所有线程共享方法区,因此必须设计为线程安全的。方法区不必固定大小, 不必连续,可以在一个堆中自由分配,允许指定初始大小以及最小最大尺寸。存储的信息包括: a) 全限定名 b) 直接超类全限定名 c) 类还是接口 d) 访问修饰符 e) 直接超接口全限定名的有序列表 f) 该类型常量池 g) 字段信息 h) 方法信息 i) 除了常量外所有类变量(静态) j) 一个到ClassLoader的引用 k) 一个到Class的引用
5. 堆
运行时创建的所有类实例和数组放在同一个堆中。一个Java虚拟机实例中只有一个堆空间,所有线程共享。堆区也不必连续,可以动态扩展或者收缩。对象的内部表示有虚拟机实现者决定。
数组页拥有一个与他们的类相关联的Class实例,具有相同维度和类型的数组都是同一个类的实例。
6. 程序计数器
运行中的Java程序每一个线程都有自己的PC。大小是一个字长,可以持有一个指针,也能持有一个returnAddress。 当线程执行某个Java方法时,PC寄存器的内容总是下一条将被执行指令的“地址”,可以是一个本地指针也可以是在方法字节码中相对该方法起始地址的偏移量。如果正在执行本地方法,则值为“undefined”。
7. Java栈
每当启动一个新线程的时候,Java虚拟机都会为他分配一个Java栈。Java以帧为单位保存线程的运行状态,虚拟机只会以帧为单位压栈或者出栈。正在执行的方法称为该线程的当前方法,当前方法使用的栈帧称为当前帧,当前方法所属的类称为当前类,当前类常量池称为当前常量池。当虚拟机遇到栈内操作指令的时候,对当前帧内数据执行操作。
Java方法可以以两种方式完成:通过return返回;抛出异常中智。不管哪种方式,虚拟机都会弹出当前Java栈然后释放。栈上的所有数据都是此线程私有的。
Java栈在内存中也不必连续,可以分布在连续的栈里,也可以分布在堆里,或者两者兼而有之 。
8. 栈帧
栈帧由三部分组成:局部变量区,操作数栈和帧数据栈。
局部变量区 被组织为一个以字长为单位、从0开始计数的数组。字节码通过从0开始的所以来使用其中的数据。类型为int,float,reference和returnAddress的值在数组中只占一项。类型为byte,short和char值在存入数组前都将被转换为int值,在栈中都当作int处理,只有存回堆或者方法区时才转换为原来的类型。long和double的值在数组中占据连续两项。局部变量区包含对应方法的参数和局部变量。实例方法中隐含reference类型的变量this。方法参数按照声明顺序放到局部变量数组中,而局部变量可以任意决定放置顺序,甚至可以一个索引指代两个局部变量。
操作数栈 和局部变量区一样,组织为字长为单位的数组,数据存储也类似,但是不是通过索引访问,而是通过压栈和出栈来访问。Java虚拟机没有寄存器,程序计数器也无法被程序指令直接访问,因此指令主要是从操作数栈中而不是从寄存器中取得操作数。也可以从字节码流中跟随在操作码之后的字节或者常量池中取得。
帧数据区 除了局部变量区和操作数栈外,还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些信息都保存在帧数据区中。
9. 本地方法栈
大小不比固定,可以动态收缩或者扩展。线程调用本地方法的时候,虚拟机保持Java栈不变,不再压入新的帧,动态连接并直接调用指定的本地方法。 传递给该函数的参数以某个确定的顺序压入栈,返回值也以确定的方式传回调用者。和可能本地方法接口需要回调Java虚拟机中的Java方法 ,这种情形下该线程会保存本地方法栈的状态并进入另一个Java栈。
10. 执行引擎
在Java虚拟机规范中,执行引擎的行为使用指令集来定义。
指令集 方法的字节码流是由Java虚拟机的指令序列构成的。每一条指令包含一个单字节操作码,后面跟随0个或多个操作数。虚拟机取得操作数的地方包括跟随操作码的操作数、常量池中的项、当前帧的局部变量中的值、当前帧操作数栈顶的值。执行一条指令包含的任务之一就是决定下一条要执行的是什么命令。很多指令的下一个操作码就是当前操作码和操作数之后紧跟的那个字节。另一个指令如goto和return,执行引擎决定下一个操作码时把它当作当前执行指令的一部分。假如抛出异常,执行引擎将搜索合适的catch子句,决定下一条指令。
执行技术 实现可以使用多种执行技术:解释,即使编译,自适应优化,芯片级直接执行。由实现自由选择。最有意义也最迅速的执行技术之一就是自适应优化。监视代码执行情况,判断出某个方法是瓶颈的时候,启动一个后台线程,把字节码编译为本地代码并优化。Java程序运行时特征:方法调用和动态派发。