接上文《【运行时数据区】-并发编程-前置知识(4.并发编程基础)-2》,我们得到了2个推论,并且证明了推论1:
- 推论1:此线程非彼线程——JVM层的线程应该是Java线程,操作系统层的线程是真正的系统线程。
- 推论2:此线程与彼线程之间应该存在某种映射关系——JVM层的线程,与操作系统层的系统线程之间,应该存在某种映射关系。
本文跟踪”JVM启动后,产生的JVM侧的main线程与java侧的main线程“的代码,来分析推论2。
1.JVM相关的代码
JVM启动后,会产生如下现象:
JVM本身会产生一系列的系统线程,其中1个线程表示JVM进程的main线程,这个可以通过操作系统层的工具观测到。
JVM在产生操作系统层面的main线程时,会在Java层产生对等的main线程,这个可以通过JDK的工具观测到。
与上述流程相关的JVM源码,如下图:
- main.c:JVM的入口模块
- javac.c:提供了java相关的接口
- java_md_solinux.c:可以简单地认为是java.c中一系列接口在Linux操作系统的实现*(为聚焦本文关注点,简化描述但不严谨)*
- jni.cpp:JVM层(C++语言实现)与JDK层(Java语言实现)之间的接口
- 比如:JDK中很多native接口,可以认为是JVM层与JDK层之间的接口
- thread.cpp:提供了线程的CRUD相关的接口
- os_linux.cpp:可以简单地认为是thread.cpp中一系列接口在Linux操作系统的实现*(为聚焦本文关注点,简化描述但不严谨)*
- vmThread.cpp:比较杂,可以认为是辅助的线程管理模块
- java_md_common.c:比较杂,可以认为是诸如解析java.exe的命令行参数解析等的辅助功能*(为聚焦本文关注点,简化描述但不严谨)
2.JVM的开始-main函数
- 关键源码:
- 代码解读:
- JVM的入口:main.c中有
main
函数,这就是JVM的入口 main
函数的主体代码不关键:main
函数主体代码就是适配不同操作系统的业务逻辑,这部分不关键main
函数最后1行代码是关键:调用了java.c的JLI_launch
函数
- JVM的入口:main.c中有
- 小结:从这一段代码我们可以覆盖了下图红框中的流程
3.初现端倪-JLI_Launch函数
3.1.JLI_Launch函数主流程
JLI_Launch
函数的主流程如下图红色线:
- 流程粗读:笔者先宏观性地理解一下上述流程
- 流程开始:JVM的
main
函数初始化JVM的环境,这部分在整体流程中属于辅助流程。 - 流程核心1:加载真正的JVM,JVM核心代码是编译到
libjvm.so
中的,这部分是整体流程的重要流程。 - 辅助流程:完成流程核心1后,JVM做了一些辅助任务,这部分是整体流程的辅助流程。
- 流程核心2:创建新的线程,这部分也是整体流程的重要流程。
- 流程开始:JVM的
3.2.初始化执行环境
- 关键代码:
代码解读:
- 在
CreateExecutionEnvironment
函数位于java_md_solinux.c
中。 - 此函数检索并判断jre的路径、libjvm.so路径、jvm.cfg路径。jre是大家比较熟悉的,我们来看看libjvm.so和jvm.cfg
- jvm的核心代码被编译到libjvm.so中,以动态库的形式提供给JVM各模块调用。
- jvm.cfg内容如下:
- 在
小结:这部分流程不太关键,快速了解即可
3.3.加载JVM
- 关键代码:
代码解读:在java_md_solinux.c的
LoadJavaVM
函数有关键的2步- 关键1:通过
dlopen
函数加载libjvm.so
动态库。 - 关键2:通过
dlsym
函数确认并获得libjvm.so
中的3个函数句柄,通过这3个函数句柄的名字大致可知是对JVM的创建和获取:- JNI_CreateJavaVM
- JNI_GetDefaultJavaVMInitArgs
- JNI_GetCreatedJavaVMs
- 关键1:通过
小结:此段流程是后续流程的基础,可以跟踪一下JVM源码详细理解一下:
3.4.java.exe的参数/配置等辅助设置
- 关键代码:
代码解读:这部分代码属于辅助流程,根据上述代码截图,基本可以自行理解,此处不赘述
小结:代码阅读理解到这里,我们当前在流程的这个地方(下图红线处):
3.4.创建新线程
- 关键代码:
- 代码解读:
- 当流程执行到java_md_solinux.c的
JVMInit
函数时,内部会调用java.c的ContinueInNewThread
函数 - java.c的
ContinueInNewThread
函数会调用java_md_solinux.c的ContinueInNewThread
函数 - java_md_solinux.c的
ContinueInNewThread0
函数的内部实现便是创建系统线程、Java线程的关键点了- 此函数调用
pthread
库函数,创建Linux上的系统线程,这个就是JVM进程中的main线程
- 此函数调用
- 当流程执行到java_md_solinux.c的
- 小结:本章节解读到下图红色线的流程,至此,我们就找到了JVM启动时与main线程创建的入口了
4.平行世界-JavaMain
根据第3章节,JVM会创建系统线程main
,而main
线程的线程回调函数就是JavaMain
函数,从这个函数名我们大致可以猜测这个函数就代表在JVM层面看到的Java代码层的包含main函数的主类。
我们接下来详细分析一下JavaMain
函数:
4.1.JVM创建JavaThread对象
- 关键流程:这条调用链比较复杂,也很关键,关键就在这里:
- java.c的JavaMain()函数:调用
InitializeVM
函数
- java.c的InitializeJVM(): 调用jni.cpp的JNI_CreateJavaVM函数。
- jni.cpp的JNI_CreateJavaVM():调用thread.cpp的Threads::create_vm()
- thread.cpp的Threads::create_vm():这里JVM创建了名为
JavaThread
的对象,这个对象表示站在JVM层维护的Java线程对象。- 这个对象表示Java代码层的
main
线程。 - 创建了这个对象后,调用了thread.cpp的
set_as_starting_thread()
- 这个对象表示Java代码层的
- thread.cpp的
set_as_starting_thread()
:调用os_linux.cpp的create_main_thread()
,传入创建好的JavaThread
对象。
- os_linux.cpp的
create_main_thread()
:调用os_linux.cpp的create_attached_thread()
- os_linux.cpp的
create_attached_thread()
:创建OSThread
对象,记录当前所在的线程对象记录到OSThread
对象中,- 当前所在的线程对象就是第3章中讲到的JVM层的系统线程
main
线程。
- 当前所在的线程对象就是第3章中讲到的JVM层的系统线程
- 小结:此时在JVM层,已经在操作系统层面创建了2个线程,1个线程是JVM进程自身的主线程,另1个线程就是JVM为Java代码的
main
线程创建的操作系统线程。
4.2.JVM创建Java代码侧的Java线程
- 关键流程:
- thread.cpp的
create_initial_thread()
:JVM通过JNI,创建JDK的java.lang.Thread
类的klass
对象,进而创建了Java代码侧的java.lang.Thread
对象,最后将这个klass
对象设置到OSThread
对象中。
- 小结:至此,站在JVM层,JVM维护了1个
OSThread
对象,这个对象中维护Java的main
线程对象和系统线程对象- Java的
main
线程对象:- 在Java代码侧,Java程序猿看到的是
java.lang.Thread
对象 - 在JVM侧,对应的就是
klass
对象
- 在Java代码侧,Java程序猿看到的是
- Java的
5.总结
回到本文一开始的2个推论:
- 推论1:此线程非彼线程——JVM层的线程应该是Java线程,操作系统层的线程是真正的系统线程。
- 推论2:此线程与彼线程之间应该存在某种映射关系——JVM层的线程,与操作系统层的系统线程之间,应该存在某种映射关系。
本文跟踪了JVM创建Java进程的main
线程的全流程,可以将推论2进一步明确:
- 推论2(New):从JVM层观测到的线程是Java代码层的Java线程,从操作系统层观测到的JVM层相关的系统线程中,有1个系统线程就对应Java层的Java线程。
- JVM层用1个
OSThread
对象维护了pthread
创建的系统线程和对应的java.lang.Thread
的klass
对象。 - JVM层的
java.lang.Thread
的klass对象
在Java层还对应1个java.lang.Thread
对象,这两个对象在各自的层次中表示Java线程。
- JVM层用1个
6.感慨:知识的尽头
玩了1个多月chatGPT,刚开始只认为它不过是AI发展史中的涟漪,谁知chatGPT可能是历史的某个里程碑:
- 笔者在软件行业从业有18年,初次阅读并梳理本文涉及到的"JVM的Java线程与操作系统线程的关系"大约花了4天的时间
- 出于好奇,把这个问题抛给了chatGPT,愕然!
经常感慨计算机世界的知识浩渺无尽,自己却那般无知渺小。
随着文明的发展,浩渺无尽是相对的,对于我是无尽,对于AI可能是有穷。
附上chatGPT对Java主线程与系统线程关系的答案: