Java虚拟机体内部系结构包括class文件、类装载子系统、运行时数据区、执行引擎、本地方法调用结构,其中运行时数据区包括方法区、堆、Java栈、程序计数器、本地方法栈等。具体结构以下图所示(摘自Inside Java Virtual Machine): 算法
1. class文件多线程
在Java中,全部源文件都编译成二进制的字节码,而后由虚拟机装载运行。通常这样的字节码是以class文件的形式存在。在运行时,由ClassLoader类(System ClassLoader or User-defined ClassLoader)找到对应的class文件,读取其中的字节码,而后交由虚拟机解析运行。ide
在class文件中,包含了定义一个类或接口的全部信息,包括类名、访问权限、父类名、继承的全部接口、全部字段、全部方法、方法中的代码、属性等信息,而且每一个class文件的开头还包含了魔术值和版本信息,魔术值用以标识当前的字节码是合法的字节码,版本表示生成当前字节码的编译器版本,从而虚拟机获知其版本而作特定处理,若是对于虚拟机不支持的字节码版本号拒绝加载。spa
在class文件中,不少信息都是以字符串的形式存放,好比对外部类成员或方法的引用,这些字符串信息在连接的时候由虚拟机解析。每一个Java类,无论是包成员类仍是内部类都会生成一个单独的class文件,于是class文件是相对独立的。详细信息参考class文件格式。线程
2. 类装载子系统设计
类装载子系统负责查找class文件,读取字节码,作部分简单的检验,如魔数是否正确,版本是否受支持,各类数据格式是否正确等。部分解析后的字节码数据存放到方法区中,最后建立字节码表明的类或接口的Class实例。指针
在Java中,类装载系统是经过ClassLoader来完成的。虚拟机规范中,定义了启动类装载器和用于定义类装载器。在sun提供的虚拟机中,包括了启动类装载器、扩展类装载器、系统类装载器、用户定义类装载器。他们以父子链的方式组织在一块儿。除了启动类装载器,其余的装载器都是ClassLoader的子类。ClassLoader定义了一些方法能够帮助用户定义本身的类装载器,如defineClass等。详情参考Java中的ClassLoader。调试
如何卸载类数据?(第七章)orm
3. 运行时数据区对象
运行时数据区保存了全部在运行时的信息。包括方法区、Java栈、堆、程序寄存器、本地方法栈等。其中方法区和堆只在虚拟机中保存一份实例,于是须要处理多线程的同步问题;Java栈、程序寄存器是每一个线程中有单独的实例,于是对不一样的线程,他们的数据是私有的。
3.1 方法区
方法区中保存了读取的字节码信息(包括常量池,静态方法和静态成员信息)、字节码表明的Class类实例、一个指向加载它的ClassLoader实例。
Java程序能够有两种方式来获取某个类的Class实例:
1. Class.forName()方法
2. Object.getClass()方法
经过Class实例获取和该类或接口相关的任何信息。参考Class类的定义。
(注:对有启动ClassLoader加载的类,Class方法中的getClassLoader方法返回null)
为加快执行速度,能够在方法区中引入方法表机制,记录能被外界调用的该类的实例方法,包括父类中继承下来的方法。(第八章详细介绍?)
方法区中根据类名搜索类信息,算法:散列、搜索树等。
3.2 Java栈
虚拟机为每一个线程生成一个Java栈,于是对不一样的线程,栈内的数据都是私有的。Java栈由栈帧组成,Java栈的操做只有两种,压入栈帧和弹出栈帧。线程中每一个方法的调用都会在Java栈压入一个栈帧;每次方法返回(正常方法或抛异常返回),该方法对应的栈帧都会从栈中弹出。
3.2.1 栈帧
栈帧由操做数栈、局部变量区和栈帧数据组成。因为Java中的指令是基于栈而设计的,于是不少指令的默认操做数就是操做数栈中的数据。操做数栈用于保存指令的操做数和指令操做后的结果。
局部变量区用于保存当前方法的局部变量。
栈帧数据区则保存当前栈帧的信息,如指向当前类常量池的指针,用于操做数为常量池索引的指令;还有一些和特定虚拟机实现相关的信息和调试信息。
3.3 程序寄存器
每一个线程在执行时都会保存当前指令的下一条指令的地址,以控制程序的之行流程。
3.4 堆
堆保存了程序在运行时的全部对象。在Java中,全部的对象都是保存在堆中的,而外部经过对象的引用来访问对象。因为Java存在垃圾回收器,于是Java对象可能被移动,以减小内存碎片。其中一种实现能够很好的解决移动对象而须要改变全部该对象的引用变量的技术,即将堆分为句柄池和对象池。对象池中的对象保存了对象的真正内容,而句柄池中的项包含两个指针,一个指向对象,一个指向类数据。一个对象引用就是指向句柄的对象指针。这样当须要移动对象时,对象的地址更改之后,就不须要将每一个引用的值都进行更改,只要改变句柄池中指向对象的指针值便可。然而这种设计是以牺牲速度为代价的,由于这样每次访问对象就要多经历一次指针定位。
在某些垃圾回收器实现中,对象须要额外的信息,若是引用计数的垃圾收集器,须要为每一个对象记录引用计数信息;而对另外有些机制,则可能须要暂时保存某些数据。这些额外的数据能够保存在类中,也能够在记录在其余地方。相似的还有同步机制中的数据和记录是否已经调用过finalize方法的信息。
在Java中有指令用于在内存中分配对象,却没有显式的指令来释放内存中的对象。
3.5 本地方法栈
当Java方法调用本地方法的时候,当前线程的程序寄存器是不肯定的值。程序的执行也转向本地方法。本地方法能够正常返回,也能够抛出异常。抛出的异常会在调用该本地方法的指令中从新抛出。
4. 执行引擎
每一个用户线程(即不包括垃圾回收线程等)都有一个执行引擎实例,用以执行字节码指令。
5. 本地方法接口
Java程序能够经过本地方法接口来调用本地方法。