引言
JVM(Java Virtual Machine)Java虚拟机的概念大家都不陌生,Java之所以可以做到“一次编译,到处运行”的跨平台性,其根本原因就在于JVM。JVM是建立在操作系统(OS)之上的,Java虚拟机屏蔽了开发人员与操作系统的直接接触,我们在通过Java编写程序时,只需要负责编写Java代码即可,关于具体的执行则会由JVM加载字节码后翻译成机械指令交给OS执行。
同时JVM方面的知识,在如今Java内卷日益加重的时代中,也变成了每位Java开发人员必须要掌握的一项技能,求职面试过程中,Java虚拟机也是面试官必问的话题。
一、初始Java虚拟机JVM
关于JVM是什么?在网上有很多官方而死板且难以让人理解的介绍,在我看来,简单的去理解它就是:一个架构在平台上的平台。这句话具体是什么意思呢?在Java之前,无论是C语言还是汇编等这类的底层语言开发出的程序,在不同的环境/系统下都需要进行不同编译后才能执行的。而Java则反其道而行之,不再需要根据不同的环境而改变自己的代码,而是自己架构出了一个平台,开发人员编写出的代码只需要能够在自己平台上执行即可,完全屏蔽了开发者与操作系统的接触,而这个Java自身架构出的平台则被称为JVM。同时,任何一门语言只要能够转换成class
文件,符合都能够被JVM接受并执行。
举个例子理解就是:类似于C、汇编这类的底层语言属于“墙头草”,代码需要根据不同的环境进行改变。
而Java则因为JVM的原因则变成了“屋顶花”,无论环境怎么变,代码都不需要跟着改变。
如下图所示:
同时值得注意的一点是:JVM不仅仅能够具备跨平台性,也同样存在跨语言性,只要别的语言写出来的代码最终转换为JVM可识别读取的
.class
文件,那么JVM也依旧可以执行它。
1.1、JDK、JRE与JVM之间的关系
而JVM是属于JRE的,而在我们学习Java之初就曾学到:JDK、JRE与JVM几个名词,那么它们之间又是什么关系呢?
- JDK(Java Development Kit):Java的SDK开发工具包,包含工具包、JRE、JVM在内
- JRE(Java Runtime Environment):Java的运行时环境,包含JavaAPI、JVM在内,提供了Java程序执行的最低的环境要求
- JVM(Java Virtual Machine):Java自身架构的虚拟计算机平台,是基于其他平台基础上进行架构的,为Java字节码的执行提供了保障
从涵盖度方面来说,JDK包含了JRE,JRE包含了JVM,而JVM则是作为Java程序执行的平台。
1.2、虚拟机的两种架构模型及JVM架构模型
目前虚拟机主要有两种架构模型,一种是基于栈式的,另一种则是基于寄存器式的。从性能上说,基于寄存器模型会比基于栈式的虚拟机性能更好,但是从移植性上看,栈式的虚拟机会远强于基于寄存器模型的虚拟机。
何谓基于栈式的虚拟机?
基于栈式指的是虚拟机在执行指令时,采用的方式是基于栈的指令集,会将需要执行的指令一条条的压入栈中,主要有入栈和出栈两种操作。基于栈式的虚拟机都会存在一个操作数栈的概念,虚拟机在真正运算时,都是通过操作数栈进行操作,与内存进行交互,简单来说就是无论任何操作,都需要通过操作数栈进行。这种模型的虚拟机最大的好处在于可以无视硬件、物理架构。当然,缺点也非常明显,因为无论什么操作都要经过操作数栈,所以性能会低一些。
Java的JVM、Python的CPython、.Net的CLR等虚拟机都是基于这种栈式模型的。
来简单感受一下基于栈式的虚拟机运算过程:
/* ------Java代码------ */
int a = 3;
int b = 2;
int c = a + b;
/* ------javap -c -v -p 查看到的字节码------ */
0: iconst_3 // 将3放入操作数栈顶
1: istore_1 // 写出操作数栈顶部元素,并将其放在局部变量表中索引为1的位置
2: iconst_2 // 将2放入操作数栈顶
3: istore_2 // 写出操作数栈顶部元素,并将其放在局部变量表中索引为2的位置
4: iload_1 // 从局部变量表中加载索引位置=1的数据值
5: iload_2 // 从局部变量表中加载索引位置=2的数据值
6: iadd // 弹出操作栈顶的两个元素并进行 加 操作(3 + 2)
7: istore_3 // 将加之后的结果刷写到局部变量表中索引为3的位置
8: return // 返回
对于如上过程中,前四条分配指令就不分析了,重点分析一下后面的运算过程,也就是c=a+b
这个过程,具体执行如下:
- ①数据
a
从局部变量表经过总线传输到操作数栈 - ②数据
b
从局部变量表经过总线传输到操作数栈 - ③数据
a
从操作数栈经过总线传输给CPU
- ④数据
b
从操作数栈经过总线传输给CPU
- ⑤
CPU
计算完成后,将结果通过数据总线传输到操作数栈 - ⑥运算结果从操作数栈经过总线传输到
CPU
- ⑦
CPU
将数据经过总线传输到局部变量表赋值给c
从如上步骤可以看出,一次简单的加法运算就需要经过七次总线传输,因为所有操作都要经过操作数栈的原因,所以效率方面会低许多。
什么又叫基于寄存器模型的虚拟机?
基于寄存器模型的虚拟机存在很多虚拟寄存器的概念,用于模拟CPU中真实的PC寄存器,但它们往往都是以别名存在,如:
R1、R2、R3....
等,在执行时,执行引擎需要对这些别名进行解析,然后找出具体操作数的位置,然后取出操作数进行操作。这些虚拟的寄存器也并不是直接在CPU中的,而是和操作数栈一样,位于运行时栈中,通过一个数组(运行时栈帧中的连续内存空间)存储所有的虚拟寄存器。
Android的Dalvik、Lua5.0的RegisterBased等虚拟机都是基于寄存器模型实现的。
还是和前面一样,通过一个简单的c=a+b
例子来理解这类基于寄存器模型的虚拟机运算过程:
// -------- Lua代码 ----------
a = 2;
b = -1;
c = a + b;
// -------- 寄存器指令 --------
LW R1,a
LW R2,b
ADD R3,R1,R2
LW
是load
的意思,代表将数据a、b
加载到虚拟寄存器R1、R2
中。同样的,重点分析一下运算的过程:
- ①数据
a
从R1
虚拟寄存器经总线传输到CPU
- ②数据
b
从R2
虚拟寄存器经总线传输到CPU
- ③
CPU
进行+
计算后将数据经总线刷写回虚拟寄存器R3
中
从如上过程中不难看出,基于寄存器模型实现的虚拟机,虚拟寄存器中的数据在运算时,不需要先传输到操作数栈后再传输给总线运算,而是可以直接将数据送入物理CPU中。对比前面基于栈式的虚拟机,寄存器模型虚拟机明显的会在性能方面更占优势。因为在这种模型下的虚拟机是不存在操作数栈的概念,因此在运算时可以节省很多数据出/入栈(pop/push)的指令,也就代表中传输数据时会少很多的总线传输次数。
总的来说,基于栈式实现的虚拟机,因为要基于操作数栈进行操作,所以会有很多数据出栈/入栈的指令,指令数量会比寄存器模型下的虚拟机多出很多。而基于寄存器的指令是直接交给CPU在高速缓存区(L1/L2/L3)执行的,性能方面来说会更胜一筹。但因为基于寄存器的指令集是与硬件架构紧密关联的,不同的CPU硬件架构下的指令会有些许不同,所以无法做到可移植。
Java的虚拟机JVM是基于栈式模型实现的,因为操作数栈的存在,所以不需要特别依赖于硬件支持,所以具备优异的跨平台性。但“成也萧何败也萧何”,因为所有操作都需要经过操作数栈再与内存交互的原因,所以执行效率并没有那么高效,性能方面会有些慢。
但在目前的主流JVM中,都使用了JIT这类的即时编译以及热点探测技术,可以将一些经常运行的Java代码直接转化为机械码(机械指令)保存,以后每次执行这些热点代码时并不会再通过字节码方式运行,而是直接通过执行机械指令的方式操作。从而使得Java在具备跨平台性的同时也能够保证性能。
1.3、Java虚拟机的生命周期
JVM的生命周期一般会分为启动、运行以及退出这三个阶段:
- 启动:
Bootstrap
加载器创建初始类,对JVM进行初始化并创建一个进程 - 运行:创建进程后的正常执行阶段,平时所说的Java程序实际上就是一个JVM进程
- 退出:进程终止的阶段,主要会分为如下几种情况:
- ①程序正常执行结束退出
- ②执行过程中出现错误导致异常终止退出
- ③操作系统错误导致程序终止退出,如内存不足、系统崩溃等
- ④程序中使用代码手动退出,如调用
System.exit
或Runtime.exit/halt
方法等 - ⑤JNI中加载或卸载虚拟机时退出
JVM的生命周期实际上与普通进程的生命周期一致,因为JVM对于操作系统而言,也仅是一个进程而已,JVM与其他程序的进程都会被“一视同仁”,不会有其他差异。
二、Java虚拟机的前世今生 - JVM发展史
每当谈到JVM都会扯出很多话题,诸如JVM内存分区、GC分代/回收策略、JVM调优等等,而这些话题默认都是建立在HopSpot虚拟机上的,因为Oracle官网上下载的JDK中默认嵌入的虚拟机便是它。
但其实也可以自研JVM,只需符合Oracle官方给出的虚拟机的规范文档即可,如国内外的一些大厂阿里、IBM、谷歌等,生产环境中应用的JVM都属于自研的。
从Java语言的出现直至如今,曾经涌现和湮灭过许多经典的虚拟机,接下来让我们来看看Java虚拟机家族的发展轨迹。
2.1、开创世纪 - Sun Classic VM
- 发布日期:1996年1月
- 研发出品:Sun公司
- 属性:开创世纪(作为世界上第一款Java商用虚拟机发布)
这款虚拟机是无法混合解释器和编译器一起工作的,也代表着要么采用运行速度很慢的解释器执行,要么采用编译过程很长的编译器运行。同时在这款虚拟机中,官方也仅提供了解释器,如果要采用编译器的话还需要额外独自实现。
2.2、英雄气短 - Sun Exact VM
- 发布日期:1998年12月
- 研发出品:Sun公司
- 属性:英雄气短(为后续的虚拟机打下了基础,属于承上启下的角色)
这款虚拟机是Sun公司在JDK1.2中发布的,主要目的就是为了解决Sun Classic VM存在的各种隐患问题,同时这款虚拟机也是初步具备了现代高性能虚拟机中的雏形,其中首次实现了两级即时编译器、编译器+解释器混合工作模型两种思想。Exact对比Classic来说,同时也采用了更加精准的内存管理方案-确切式内存管理方案,可以让虚拟机知道内存中某个位置上的数据具体是何种类型,大大的提高GC回收效率。
不过比较有意思的是:这款虚拟机对比之前的ClassicVM,虽然技术上先进了很多,但是很快就被后面更加优秀的HotSpotVM代替掉了,真可谓是“英雄气短”。
2.3、世界最快 - BEA JRockit VM
- 发布日期:2001年2月
- 研发出品:BEA公司
- 属性:世界最快(专注于速度的虚拟机,可以将响应时间细化至毫秒/微妙级别)
这款虚拟机是BEA公司在2001年2月正式发布的,专注于服务端应用,不太关注程序的启动速度,全部代码会被编译执行。同时它提供了全面的Java运行时解决方案:JRockit Real Time与MissionControl组件,前者可以将响应速度精确控制在毫秒、微妙级别,对于财政金融、军事指挥、电信网络等类型的项目拥有良好的支持,后者则属于服务型组件,可以使用极底的资源开销来管理、监控和分析生产环境中的Java程序。
2008年BEA公司被Oracle收购,JDK1.8中的HotSpot移植了JRockit的优秀特性。现在已经成炮灰了,湮灭在了历史的长河之中。
2.4、武林霸主 - Sun HotSpot VM
- 发布日期:2000年5月
- 研发出品:Sun公司
- 属性:虚拟机一哥(JDK默认、综合性能最好、目前服务端应用最广泛的虚拟机)
这款虚拟机最开始并不是由Sun公司研发的,而是由LongviewTechnologies的公司设计出的一款非应用于Java程序的虚拟机,1997年被Sun公司收购,整合了其优秀特性(JIT即时编译、热点代码探测等),最终得到了HotSpotVM。这款虚拟机最大的特点是可以通过执行计数器,在Java程序运行过程中找出最具有编译价值的代码(执行次数够多的代码),然后通知JIT编译器进行编译,通过编译器和解释器的一同合作,在程序响应时间和执行性能中取得最佳的平衡点,这样即避免了JRockit的编译时间过长问题,也解决了Classic这类虚拟机执行效率过慢问题。
这款虚拟机是服务器应用最广泛,并且也是最为特殊的虚拟机。因为Oracle在2008和2009两年,分别收购了BEA和Sun公司,得到了两款优秀的Java虚拟机:JRockit 和HotSpot,而Oracle将这两款虚拟机的优势进行移植整合,打造出了更加优秀的HotSpot VM,也就是JDK1.8中内嵌的虚拟机,或许我们将它称为“HotRockit”会更加合适。
2.5、独树一帜 - IBM J9 VM
- 发布日期:2017年9月
- 研发出品:IBM公司
- 属性:独树一帜(IBM相关软件、硬件、平台捆绑发布的虚拟机)
IBM J9 VM的市场定位与HotSpot差不多,但J9是一款设计上从服务器到桌面程序再到嵌入式全面考虑的多用途虚拟机。但因为与Oracle有协议,IBM不允许私自发布单独的J9虚拟机,所以一般应用J9虚拟机的多是IBM自己的产品或平台,如IBM的WebSphere产品、AIX/Z操作系统上部署的Java程序等。所以一般J9是作为IBM内部应用的虚拟机使用的,但在2017年9月开源贡献给了Ecalipse基金会,命名为OpenJ9。
2.6、性能魔兽 - Azul Zing VM
- 发布日期:2010年
- 研发出品:Azul Systems公司
- 属性:性能魔兽(可管理上TB级别堆内存、低延迟高吞吐、GC最流畅的虚拟机)
Azul Zing VM是之前Sun还未被收购时,从HotSpot虚拟机上fork的一个分支,但经过多年的内部自研,修改了虚拟机内的许多细节实现,同时也为它重新写了一套GC,支持几TB量级的堆空间管理。同时也配套了ZVision/ZVRobot这类的性能监控工具,可以方便用户监控JVM的运行状态,比如锁状态竞争、对象分配过程、热点代码探测等细节方面都能进行监控。
总的来说,Zing拥有高吞吐低延迟、快速预热、易于监控的特性,但很遗憾的是:它收费,并且价钱不菲。不过它也有一个开源版本:Zulu VM。
2.7、腹死胎中 - Apache Harmony VM
- 研发出品:Apache基金会
- 属性:腹死胎中(Java史上最惨的虚拟机)
这款虚拟机比较有意思,本身背景也比较强,典型的JVM家族中的“富二代”,IBM和Intel联手开源的JVM,背后有世界两大顶级大厂支持。本身是有望成为F/OSS系的主流虚拟机的,可无奈被Sun公司坑了一把,最终腹死胎中。
Harmony是Java的开源实现,但由于许可上的纠纷,Harmony项目永远不可能自称为Java虚拟机或是Java核心语言库的实现。Harmony项目也一直在努力争取获得JCP(Java Community Process)的JCK授权。但是,由于Sun公司的态度,JCP仅仅允许授权给Harmony一个带有限制条件的TCK,即仅仅能使用在J2SE,而不是所有Java实现上(包括J2ME和J2EE)。两者之间自始发生冲突。当Oracle掌权Java后,这一冲突继续延续。最终Sun公司阴的这手,硬活生生的把拥有“美好前程”的Harmony拖死了。
这事还有后续,在2010年12月,Apache基金会不满JCP组织的处理,直接宣布退出了JCP组织。
2.8、别出心裁 - Kilobyte VM与CDC/CLDC HotSpot VM
Kilobyte VM简称KVM,CDC/CLDC则是HotSpot对标它,针对JavaME这一产品线的版本。这两虚拟机与其他虚拟机不同,之前的虚拟机都是建立在OS的基础上架构出一个可执行平台,而KVM则是属于内核虚拟机,虚拟机可直接连接/操控键盘来执行程序。主要应用与智能控制器、传感器、老人/按键手机等产品上。
2.9、百家争鸣 - 其他虚拟机
在Java的高速发展期阶段(1990-2008年左右)实现各自的虚拟机的企业,简直可谓是百家争鸣。期间惊鸿一现过许多优秀的虚拟机,但当Orcale收购掉Sun和BEA公司后,Java虚拟机的百家争鸣期结束,最终由Orcale整合了HotSpot、JRockit优良基因的“HotRockit”版HotSpot登上了霸主地位。
直至如今,可选择的虚拟机类型并不算多了,但到目前还能存活下来的虚拟机都有各自的优势。下面再给出一些涌现过的虚拟机(不一定全部被淘汰了,有些目前还在使用):
- TaoBao VM:淘宝基于HotSpot开发的定制版超高性能虚拟机
- Dalvik VM:谷歌开发的虚拟机,用于安卓系统,没有遵循Java虚拟机规范开发的
- 应用于Android 5.0之前,编译后生成的是dex文件,基于寄存器模型实现
- Android 5.0之后谷歌推出了一款新的虚拟机,ART VM代替它
- Lemur VM:阿里云开发的可以同时兼容Android与Java应用的虚拟机
- Squawk VM:Sun公司之前专门为小型嵌入式环境开发的虚拟机
- Liquid VM:BEA公司开发的一款直接操控硬件的VM,可以避免用户态到内核态的切换
- Microsoft VM:微软当时为了在IE3中支持Java应用的运行而开发的虚拟机
- Jikes RVM:IBM开发出的专门用于研究JVM的元循环虚拟机(学术版:用于研究)
- Maxine VM:Oracle和原Sun公司成员研发出的元循环虚拟机,也是同样的作用
- Graal VM:Oracle最新开源的全栈跨平台虚拟机,可以任何语言的执行平台使用
- ........
在上面我们提到过一个概念:元循环虚拟机,这是什么意思?元循环虚拟机指的是用语言本身写虚拟机,也就是用Java开发出的JVM。
ok~,JVM家族的发展史至此就暂且打住了,相对来说,目前在J2EE领域占绝对地位的肯定是HotSpot,而在J2ME领域的龙头必然是J9,无可厚非。
其实从Oracle最新发布的Graal虚拟机来说,未来的虚拟机的奋斗方向肯定是朝着更加精确的模块化、更强的GC机制、混合多语言执行以及多核并行执行的目标不断前进的。
三、Java虚拟机之JVM组成架构
Java虚拟机一般是由多个模块一起组成的,主要可分为类加载子系统、执行引擎子系统、运行时数据区、垃圾回收子系统以及本地接口和本地方法库。在Java程序启动/执行过程中,这些模块各司其职,相互配合,组成了整个Java的执行平台 - JVM。
列出来的这些是从整体上涵盖了整个JVM进行划分的,而并不是从某个维度上进行划分,本篇中先对于这些进行简单介绍,后续文章中再依次进行深入分析。
3.1、类加载子系统
类加载子系统是用于加载编译后class
文件的,但它只负责将符合格式要求的class
字节码信息加载进内存,而只要符合格式规范的class
文件都能被加载,至于加载进入的class
文件到底是否能执行就并不是它负责的了,这是执行引擎子系统的范围之内的责任。
而类加载子系统中,核心知识点分为类加载器、双亲委派模型、类加载过程三大块。
3.2、执行引擎子系统
执行引擎子系统担任着JVM的“翻译官”角色,它负责将加载进内存的class
字节码指令“翻译”成机器语言交由硬件执行。而字节码可以通过解释器和即使编译器两种途径转换为机械指令。HotSpot
虚拟机中,采用的便是解释器+即使编译器混合执行的工作模式。
3.3、运行时数据区
运行时数据区是整个JVM中的重点,开发者编写的所有代码最终都会被加载在这里之后再开始执行。同时,Java为何能够避免像C那样,需要手动管理内存的原因之一也在于这块区域。
同时这也是整个JVM“黑盒”中,开发者能够接触到的为数不多的区域。
Java运行时数据区主要可分为PC程序计数器、本地方法栈、虚拟机栈、元数据空间(方法区)以及堆空间五大区域,这也是后续分析时的核心内容。但Java虚拟机规范对它的定义是是比较抽象的,运行时数据结构上的细节方面在JVM规范中并没有明确指出,大多数都是由JVM具体的实现厂商自行决定。
3.4、垃圾回收子系统
垃圾回收子系统在Java虚拟机规范中,其实没有明确规定必须要实现,但在没有发明无限的内存硬件之前,绝大部分的JVM实现时,都是有垃圾回收模块的。GC模块的主要任务就是帮助Java程序管理内存,对于垃圾对象的清除、存活对象的管理以及内存碎片的回收等工作,都交由GC系统负责。
同时Java能够做到避免像C那样需手动管理内存的最终原因也得益于GC机制,GC机制自动帮开发者管理了内存,从而屏蔽了开发人员和内存的直接接触。
JVM的GC机制里面重点知识点分为三块:垃圾回收器、垃圾回收算法、GC调优。
3.5、本地接口和本地方法库
本地方法接口的作用主要是为了融合不同的编程语言为Java所用,它的初衷是融合C/C++程序。因为在Java诞生时,正是C语言横行的时候,要想立足就必须要有一个能够调用C代码的模块,于是就专门在内存中开辟了一块区域处理标记为native的方法。它在执行时,而本地方法接口的作用则是将本地方法栈中登记的被调用的本地方法,在执行引擎处理时,将C编写的native方法从本地方法库中加载出来。
简而言之,本地方法接口就是一个Java调用非Java代码的接口,这个非Java代码一般泛指C语言所编写的本地方法库中的函数。
OK~,至此简单的分析了一下JVM的架构组成,先让大家对JVM的整体有个了解。
JVM是一个架构在平台上的平台,其中被分为了不同的模块,虚拟机在运行过程中则是通过这些模块相互配合,从而做到了执行Java代码。不过值得注意一提的是:JVM的知识点虽然比较多,但开发过程中,能够被开发者直接接触的区域却是少之又少,往往所谓的JVM调优,也仅仅只是对于GC和运行时数据区进行优化。当然,也包括JVM的配置优化,根据不同的机器、项目,在启动时,指定对应参数开启最佳的配置。
四、HotSpot虚拟机源码
因为目前大部分部署的Java程序都是默认使用HotSpot虚拟机,所以后续的文章中大部分的内容也会建立在HotSpot虚拟机的基础上进行分析。如果有效果对于它的源码比较感兴趣,也可以去Oracle官网下载,但官网上下载时会经常连接中断,所以附上网盘资源链接/提取码:m3gm(注:下载的是Open-JDK1.8的源码)。同时再源码目录结构,如下:
├─agent Serviceability Agent的客户端实现
├─make 用来build出HotSpot的各种配置文件
├─src HotSpot VM的源代码
│ ├─cpu CPU相关代码(汇编器、模板解释器、ad文件、部分runtime函数在这里实现)
│ ├─os 操作系相关代码
│ ├─os_cpu 操作系统+CPU的组合相关的代码
│ └─share 平台无关的共通代码
│ ├─tools 工具
│ │ ├─hsdis 反汇编插件
│ │ ├─IdealGraphVisualizer 将server编译器的中间代码可视化的工具
│ │ ├─launcher 启动程序“java”
│ │ ├─LogCompilation 将-XX:+LogCompilation输出的日志(hotspot.log)整理成更容易阅读的格式的工具
│ │ └─ProjectCreator 生成Visual Studio的project文件的工具
│ └─vm HotSpot VM的核心代码
│ ├─adlc 平台描述文件(上面的cpu或os_cpu里的*.ad文件)的编译器
│ ├─asm 汇编器接口
│ ├─c1 client编译器(又称“C1”)
│ ├─ci 动态编译器的公共服务/从动态编译器到VM的接口
│ ├─classfile 类文件的处理(包括类加载和系统符号表等)
│ ├─code 动态生成的代码的管理
│ ├─compiler 从VM调用动态编译器的接口
│ ├─gc_implementation GC的实现
│ │ ├─concurrentMarkSweep Concurrent Mark Sweep GC的实现
│ │ ├─g1 Garbage-First GC的实现(不使用老的分代式GC回收器)
│ │ ├─parallelScavenge ParallelScavenge GC的实现(server VM默认,不使用老的分代式GC框架)
│ │ ├─parNew ParNew GC的实现
│ │ └─shared GC的共通实现
│ ├─gc_interface GC的接口
│ ├─interpreter 解释器,包括“模板解释器”(官方版在用)和“C++解释器”(官方版不在用)
│ ├─libadt 一些抽象数据结构
│ ├─memory 内存管理相关(老的分代式GC框架也在这里)
│ ├─oops HotSpot VM的对象系统的实现
│ ├─opto server编译器(又称“C2”或“Opto”)
│ ├─prims HotSpot VM的对外接口,包括部分标准库的native部分和JVMTI实现
│ ├─runtime 运行时支持库(包括线程管理、编译器调度、锁、反射等)
│ ├─services 主要是用来支持JMX之类的管理功能的接口
│ ├─shark 基于LLVM的JIT编译器(官方版里没有使用)
│ └─utilities 一些基本的工具类
└─test 单元测试
如上便是HotSpot源码中各个目录的具体含义。
五、总结
本章简单的概述了一下JVM的大体构成,并没有对每个细节点进入深入分析,本章的宗旨在于:先简单认识JVM,后续的文章中会继续深入分析。