HotSpot VM主要分为三个部分,VM运行时,JIT编译器,内存管理器。其中详细的关系通过阅读其实没有很好的理清楚,但是这并不是书本的重点我就不进行记录了。
早期的hotspot VM是32位的,所以内存空间被限定在4G(2的32次方个地址位空间),但是其实在运用中java堆的大小还会受限于底层的操作系统的限制。
随着时代和技术的发展,64位hotspot VM开始产生,这个就极大的拓展了JVM可以使用的java堆的大小,但是这个也导致了一部分的性能问题,这是由于在CPU中缓存高速缓存中的空间是的一定的,而由于java面向对象的思想策略所以会有大量的对象指针,而随着指针的大小从32位扩展到了64位,所以oops(普通对象指针)能够在高速缓存中的存储数量就会相应的减少,这个会导致CPU的高速缓存的命中率下降,CPU会更多的和内存进行交互这个将会导致大约8%~15%的性能损失。
在JAVA6中这个问题得到了对应的缓解,就是引入了压缩指针,这个新的特性同时继承了32位指针以及64位指针的优势。压缩指针通过对齐以及偏移量将64位指针压缩成32位进行存储。当然更多的CPU寄存器也能够避免发生寄存器卸载(将寄存器的内容转到内存中)
下面就简单的介绍一下HotSpot VM运行时的几个功能划分的部分
命令行选项:JVM运行时系统解析命令行选项,并据此配置选择什么JIT编译器以及选择何种垃圾收集器等。其中一般分为三类,标准,非标准以及非稳定。标准是在所有的JAVA虚拟机都要求实现的,非标准和非稳定都是不强制实现的。
这个命令行选项我们可以理解为我们输入参数解析器,一般来说一个应用的启动将会有不少输入参数的输入,这个时候输入的参数就通过命令行选项进行解析判断并最终产生作用效果。
VM生命周期管理:这个部分负责虚拟机的启动和停止,在java虚拟机的启动和停止的步骤中有大量的细节操作。
譬如在启动的时候会:
1、解析命令行选项
2、设置对大小和JIT编译器
3、读取系统环境变量,类似于CLASS_PATH
4、如果命令行有-jar选项,启动器则从指定的jar包找入口,否则从命令行输入找入口
5、使用标准java本地接口创建第一个线程并在其中创建Hotspot VM
6、加载入口类main-class
7、通过JNI方法CallStaticVoidMethod调用main方法,并将命令行参数传给它
如果应用或者是main方法执行完毕,那么就会清理所有的未处理异常。调用本的接口方法DetachCurrentThread将main和虚拟机脱离。每次调用DetachCurrentThread的时候会导致线程数减一,所以在最终退出的时候能够确保没有正在执行的任务。
VM类加载:类加载,指类名或者接口名映射到类对象的整个过程,分为三个阶段加载、链接、初始化。一般来说在使用反射的时候会比较大概率的引发类加载,例如Class.forName(),在JVM启动时不光会加载不少的普通类,还会加载不少核心类Object、Thread之类。事实上加载阶段是Hotspot VM和特定类加载其之间相互协作的过程。
字节码验证:java为了做到类型安全在启动的时候会验证所有的类是不是都是由java进行编译产生的,一般分为两种方法类型推导以及类型检查,一般而言小于50的使用推导,大于的使用检查(检查有错会使用推导进行二次检查)
类数据共享:就是可以通过预先的加载文档生成,避免每次启动对于部分常用类的重复加载。这些类在生成了共享文档之后,每次启动JVM的时候直接读文件到内存作为数据即可,不再需要重复加载。
解释器:Hotspot VM解释器是一种基于模板的解释器,在Hotspot VM的TemplateTable中存储了解释器的信息,包含了每个字节码对应的机器代码,每个模板描述一个字节码。这个就描述了一个事实,就是java在运行的时候有一大部分是通过解释执行的,并没有和我们想象的那样编译执行。
这是由于一个事实,就是系统的大部分时间都是执行的一小部分重复的代码,所以java采取的策略就是将使用频繁的代码编译,其余代码解释执行。
异常处理:当java代码遇见运行异常的时候java虚拟机就会通知异常处理程序,一般是通过推展的方法,一直重复直到找到对应的异常处理器,并执行异常处理代码。
同步:同步机制是为了保证交替使用的资源的安全性,JVM使用minitor来实现线程运行代码之间的互斥,monitor对象只能被一个线程拥有,只有拥有这个对象的线程才被允许执行对象临界区的代码
HotSpotVM吸收了非竞争和竞争性同步操作的最新技术,极大的提高了同步的效率,通过java5中新增的偏向锁。大多数的同步操作使用称为fast-path代码的方法,由于Hotspot VM有两个编译器和一个解释器,都可以产生fast-path代码,没有竞争时就都通过fast-path进行处理,存在竞争的时候就通过slow-path代码实现。
线程管理:线程管理涉及线程从创建到终止的整个生命周期,线程管理需要管理java代码创建的线程、直接与JVM管理的本地线程以及JVM内部创建的线程。
在HotSpotVM中java线程被一一映射为对应的本地系统线程,当java线程终止的时候本地线程也被收回
JVM内部线程大概有以下几个:
VM线程
周期任务线程
垃圾收集线程
JIT编译器线程
信号分发线程
C++堆管理:除了HotSpotVM内存管理器和垃圾收集器维护的java堆意外HotspotVM还是用C/C++对存储内部对象和数据,这些类只供HotspotVM使用并不会暴露给应用。Arena及其子类是malloc之上的一层,可以快速进行内存分配。并且在JIT的编译过程中HotSpot的Client和server Jit编译器也会使用Arena
JAVA本地接口:java本地接口允许是使用其他语言编写的库进行合作操作,JNI本地方法可以用来创建检测更新java对象、调用java方法捕获并抛出异常。但是注意的是JNI可能会使应用失去一次编译到处运行的特性
致命错误处理:致命错误并不是指的代码中抛出的错误,这个错误往往指的是一些JVM内部的错误,比较常见的有outofMemoryError,当出现这种错误的时候JVM都被迫停滞,设计者认为让开发者能够诊断和修复JVM的致命错误的特点非常重要。