hotspot

HotSpot的源码目录结构

在开始正式深刻学习HotSpot的源码以前,你们首先须要明白HotSpot的源码目录结构是怎样构成的,以及每个目录中所包含的特定功能模块实现是什么。当只有在完全弄清楚这些问题后,才能更有针对性的阅读和学习HotSpot的源码。尽管HotSpot只是OpenJDK项目中的一个子集,可是HotSpot却在整个OpenJDK项目中占据了很是重要的地位,因此本书只会针对HotSpot项目的源码进行讲解,你们只需关注HotSpot便可。 java

在解压后的HosSpot源码目录下,主要由agentmakesrctest4个子目录构成了HosSpot源码的总体目录结构。其中agent目录下包含了Serviceability Agent的客户端实现。make目录下包含了用于buildHotSpot的各类配置文件。而src目录则是其中最重要的一个子目录,该目录下包含了HotSpot的全部源码实现,好比:与CPU的相关实现、与操做系统的相关实现、与平台无关的通用实现、HotSpot的核心功能实现、各种GC的实现以及执行引擎的相关实现等。至于test目录下仅仅只是包含了HotSpot相关的一些单元测试用例。 算法

在此须要提醒你们,尽管Java继承了C语言的语法结构,并改编了C++语言的对象模型,可是彼此之间仍然存在较大的语法差别。因此Java开发人员在阅读HotSpot的源码时,千万不要在语法细节上钻牛角尖,只需关注具体的数据结构和算法便可。 编程

HotSpot的源码目录结构,以下所示(目录整理自撒加大神的博文) 数据结构

├─agent Serviceability Agent的客户端实现 框架

├─make 用于buildHotSpot的各类配置文件 jvm

├─src HotSpot全部源代码 数据结构和算法

│ ├─cpu CPU相关代码 编程语言

│ ├─os 操做系相关代码 函数

│ ├─os_cpu 操做系统+CPU的组合相关的代码 工具

│ └─share 平台无关的共通代码

│ ├─tools 工具

│ │ ├─hsdis 反汇编插件

│ │ ├─IdealGraphVisualizer server编译器的中间代码可视化的工具

│ │ ├─launcher 启动程序“java”

│ │ ├─LogCompilation -XX:+LogCompilation输出的日志(hotspot.log)整理成更容易阅读的格式的工具

│ │ └─ProjectCreator 生成Visual Studioproject文件的工具

│ └─vm HotSpot VM的核心代码

│ ├─adlc 平台描述文件(上面的cpuos_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 基于LLVMJIT编译器(官方版里没有使用)

│ └─utilities 一些基本的工具类

└─test 单元测试

当你们清楚HotSpot的源码目录结构后,才能更快的熟悉HotSpot每个目录下所包含的特定功能模块实现。

2.2 Launcher简介

Launcher是一种用于启动JVM进程的启动器。在Java中,Launcher能够根据类别划分为2种。一种是正式版的启动器,也就是你们在Windows平台下常用到的java.exejavaw.exe程序。前者在运行时会保留控制台,以及显示程序的输出信息。然后者主要是用于执行JavaGUI程序,也就是说,使用javaw.exe执行Java程序时将不会显示程序的输出信息。关于Launcher的具体用法和标准选项配置,你们能够在控制台中输入命令“java -help”,以下所示:

具体用法:

java [-options] class [args...](执行类)

java [-options] -jar jarfile [args...](执行jar文件)

其中选项包括:

-d32

使用32位数据模型(若是可用)

-d64

使用64位数据模型(若是可用)

-client

选择"client"VM

-server

选择"server"VM

-hotspot

"client"VM的同义词[已过期]默认VMclient

-cp

目录和zip/jar文件的类搜索路径

-classpath

目录和zip/jar文件的类搜索路径用;分隔的目录,JAR档案和ZIP 档案列表, 用于搜索类文件

-D<name>=<value>

设置系统属性

-verbose[:class|gc|jni]

启用详细输出

-version

输出产品版本并退出

-version:<value>

须要指定的版本才能运行

-showversion

输出产品版本并继续

-jre-restrict-search|-no-jre-restrict-search

在版本搜索中包括/排除用户专用JRE

-? -help

输出此帮助消息

-X

输出非标准选项的帮助

-ea[:<packagename>...|:<classname>]

-enableassertions[:<packagename>...|:<classname>]

按指定的粒度启用断言

-disableassertions[:<packagename>...|:<classname>]

禁用具备指定粒度的断言

-esa | -enablesystemassertions

启用系统断言

-dsa | -disablesystemassertions

禁用系统断言

-agentlib:<libname>[=<options>]

加载本机代理库<libname>,例如-agentlib:hprof另请参阅 -agentlib:jdwp=help-agentlib:hprof=help

-agentpath:<pathname>[=<options>]

按完整路径名加载本机代理库

-javaagent:<jarpath>[=<options>]

加载Java编程语言代理, 请参阅java.lang.instrument

-splash:<imagepath>

使用指定的图像显示启动屏幕

你们千万不要认为Launcher就是虚拟机实现,其实从严格意义上来讲,Launcher只是一个封装了虚拟机的执行外壳,由它负责装载JRE环境和Windows平台下的jvm.dll动态连接库(Linux平台下则是装载libjvm.so)。在一个JVM的进程内部,只能执行一个指定的Java程序,也就是说,当执行多个Java程序时,也就意味着同时启动了多个JVM进程。在1.5.6小节中,本书示例了如何编译一个Debug版本的HotSpot,因此为了调试跟踪方面,你们可使用Java的另外一种启动器gamma。在HotSpotLauncher是使用C语言编写的,对比gammajava后不难发现二者的源码几乎是如出一辙的,仅存在少许差别,也就是说,在OpenJDKgammajava是共用的同一套Launcher源码实现。gamma的源码在HotSpot的源码目录下,你们能够在/hotspot/src/share/tools/launcher/java.c中找到。而java却并不是包含在HotSpot的源码目录下,而是包含在/jdk/src/share/bin/main.c中。

尽管Launcher并不是是HotSpot的核心,甚至应该算是HotSpot中比较“外围”的功能模块,但既然是这样,笔者为何还须要大费周章的对Launcher的源码进行剖析?其实了解Launcher的执行原理是很是有意义的,Launcher既然是JVM的启动器,那么必然会由它负责调用HotSpot的核心代码对JVM进行初始化,以及由它负责维护JVM的整个生命周期。因此理解了Launcher的执行原理,才是迈进HotSpot的第一步。

2.3 跟踪Launcher的执行过程

本书并不是只是一本单纯讲解HotSpot原理的理论性读物,从本章开始,你们将会从以往的枯燥和乏味中深刻到HotSpot的具体实现细节上。对于那些曾经想要深刻研究JVM技术却又止步于源码面前的Java开发人员而言,本书的知识点将会是大家迫切想要获得的答案。

尽管每个Java开发人员对Launcher的使用都很是熟悉,但这并不表明对Launcher的执行过程也了如指掌,因此本章将会重点讲解关于Launcher的执行过程并剖析源码细节。因为HotSpotLauncher是采用C语言编写的,因此具有必定的C语言功底,必然是最好不过的。但若是你仅仅只是专攻于Java技术,也并不是没法理解本章的一些源码示例,毕竟Java的语法结构就是继承自C语言,因此一些简单C语法相信你也必定可以理解。在正式开始讲解以前,笔者仍是须要再次重申一次关于源码的阅读方式,但愿你们千万不要过多停留在语法细节上,只需关注具体的数据结构和算法便可。

2.3.1 使用Launcher启动JVM

Launcher从启动到结束的整个执行链路,如图2-1所示。当成功启动Launcher后,会首先进入到Launcher的启动函数中,这一点和Java程序同样,Launcher的启动函数一样也是main()main()函数的主要任务是负责建立运行环境以及启动一个全新的线程去执行JVM的初始化和调用Java程序的main()方法。

2-1 Launcher的执行过程

main()函数成功建立运行后,就会启动一个全新的线程去调用JavaMain()函数,而JavaMain()函数的主要任务是负责调用InitializeJVM()函数。顾名思义InitializeJVM()函数确定会负责JVM初始化的相关工做,但InitializeJVM()函数自己却并不具有初始化JVM的能力,而是由它调用本地函数JNI_CreateJavaVM()去完成真正意义上的JVM初始化。

JVM初始化完成后,Launcher接着调用LoadClass()函数和GetStaticMethodId()函数,分别获取Java程序的启动类和启动方法。当这2个步骤执行完后,Launcher就会调用本地函数jni_CallStaticVoidMethod()执行Java程序的main()方法。

最后Launcher还会调用本地函数jni_DetachCurrentThread()断开与主线程的链接,当成功与主线程断开链接后,Launcher就会一直等待程序中全部的非守护线程(non-daemon thread)所有执行结束,而后调用本地函数jni_DestroyJavaVM()JVM执行销毁。在此须要提醒你们,在JDK1.2版本以前,只有主线程才容许对JVM执行销毁,而在JDK1.2及后续版本中则没有此限制,非主线程也容许对JVM执行销毁。

2.3.2 启动函数main()

Launcher成功启动后,首先会进入到main()函数中对与运行环境相关的局部变量进行初始化。初始化后的局部变量不只在程序后续建立运行环境时须要使用到,在调用JavaMain()函数时,也须要将这些变量传递过去,以下所示:

代码2-1 初始化与运行环境相关的局部变量

/* 初始化与运行环境相关的局部变量 */

相关文章
相关标签/搜索