结构
jDK 的基本目录结构为
![](https://img.haomeiwen.com/i26273155/25f5d27da389e85d.png)
corba
CORBA 全称是Common Object Request Broker Architecture,CORBA 1.1 由对象管理组织在 1991 年发布,他定义了接口定义语言(IDL)和应用编程接口(API),从而通过实现对象请求代理(ORB)来激活客户/服务器的交互。CORBA 2.0 于 1994 年的 12 月发布,他定义了如何跨越不同的 ORB 提供者而进行通讯。在功能上基本等同于Webservice,即:
- 实现了分布式系统下跨平台,跨语言的数据的传输
- 实现了远程方法的本地调用(在本地调用远程Server上的方法并取得返回值)
corba目录下为Java对该交互协议的实现,主要是Java代码。
参考
使用Java/CORBA实现分布应用编程
WebService学习总结(一)——WebService的相关概念
jaxp
JAXP的全称是Java API for XML Processing,是Java定义的一组操作XML文档的API,注意JAXP只是定义了API,并没有提供具体的实现,Java为了保证JAXP能够即装即用,默认带了Apache Xerces 解析器,代码位于jaxp/src/com/sun/org/apache下。 JAXP支持三种XML解析模型:
- DOM模型,位于jaxp\src\org\w3c\dom下
- SAX模型, 位于jaxp\src\org\xml\sax下
- STAX模型,位于jaxp\src\javax\xml\stream下,除stream目录外都是JAXP本身的API
参考
JAXP 全面介绍,第 1 部分
Java xml解析器SAX解析与StAX解析比较
学习笔记——JAXP
jaxws
JAX-WS全称是JavaTM API forXML-Based Web Services, 是一组基于SOAP协议实现的Web Service的API,与之相对的是JAX-RS,全称是JavaTM API for RESTful Webservices,是一组基于RESTful风格的Http服务实现Web Service的API。JAX-WS相关代码位于jaxws/src/share/jaxws_classes,其中com目录下主要是JDK内部实现使用的类,对外暴露的API都在javax目录下,org目录是JDK内部依赖的relaxng包,relaxng同W3C XML Schema ,DTD一样都是定义XML文档规范的一种语言。
JAF全称是JavaBeans Activation Framework, JAF的目的在于统一处理不同数据格式的方法(不管数据格式为简单文本还是由图片、声音、视频甚至其它"活动"内容共同组成的复合文档),使得Java对象与编码数据流之间的映射变得非常容易,可以参考JavaMail中JAF的应用。JAF的相关代码位于jaxws/src/share/jaf_classes下。
参考
真正的轻量级WebService框架——使用JAX-WS(JWS)发布WebService
JAF框架及在JavaMail中的应用
【webservice】Java JAX-WS和JAX-RS webservice
langtools
langtools包下都是Java语言的支持工具实现,具体如下:
src/share/classes/com/sum/javadoc: 生成API文档的接口定义,即The Doclet API
src/share/classes/com/sum/source: 解析源码的接口定义
src/share/classes/com/sum/tools/doclets和doclint: javadoc文档生成工具源码
src/share/classes/com/sum/tools/classfile: 共用的读取解决classfile的工具源码
src/share/classes/com/sum/tools/javac: javac Java代码编译工具源码
src/share/classes/com/sum/tools/sjavac: sjavac Java代码多线程和增量编译工具源码,8中为初版
src/share/classes/com/sum/tools/javah: javah JNI调用中用于生成native接口的头文件的工具源码
src/share/classes/com/sum/tools/javap:javap 查看源代码编译后字节码的工具源码
src/share/classes/com/sum/tools/jdeps:jdeps 查看Java包或者类依赖的工具源码
src/share/classes/javax/lang/model: 用于语言建模、语言处理任务和API等
src/share/classes/javax/annotation/process: 注解的处理框架
nashorn
JDK8 内嵌的Nashorn JavaScript 引擎,扩展了Java在JVM上运行动态JavaScript脚本的能力,但是因为ECMAScript规范发展较快,Nashorn相关代码很难维护,JDK11预计将移除相关代码。
jdk
其中aix,bsd,linux,macosx,solaris,windows是各平台的特定实现,其中aix,bsd,linux,solaris都是类Unix系统,基本共用solaris的实现。share目录下是平台无关的实现,具体如下:
- bin目录是JVM的启动入口
- classes目录是JDK源码
- native目录是JDK源码中native接口的实现源码,
- back目录是JDWP(Java Debug Wire Protocol ) Java调试通信协议的实现
- transport目录是建立JDWP握手连接相关的实现
- instrument目录是javaagent工具即JVMTIAgent 的实现
参考
java agent基础原理
JVMTI 和 Agent 实现
JDWP 协议及实现
启动
Jdk中java的入口函数文件为openjdk\jdk\src\share\bin\main.c中的main方法(window上为WinMain),然后调用jdk8u-dev/jdk/src/share/bin/java.c的JLI_Launch方法,启动一个jvm虚拟机;
main
作用:程序入口
位置:openjdk\jdk\src\share\bin\main.c
/*
* Entry point.
*/
//java虚拟机启动入口函数
#ifdef JAVAW //window平台入口
char **__initenv;
int WINAPI
WinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow)
{
int margc;
char** margv;
const jboolean const_javaw = JNI_TRUE;
__initenv = _environ;
#else /* JAVAW */ //linux平台入口函数
int main(int argc, char **argv){
int margc; //参数个数
char** margv; //命令参数
const jboolean const_javaw = JNI_FALSE;
#endif
#ifdef _WIN32
{
int i = 0;
if (getenv(JLDEBUG_ENV_ENTRY) != NULL) {
printf("Windows original main args:\n");
for (i = 0 ; i < __argc ; i++) {
printf("wwwd_args[%d] = %s\n", i, __argv[i]);
}
}
}
JLI_CmdToArgs(GetCommandLine());
margc = JLI_GetStdArgc();
// add one more to mark the end
margv = (char **)JLI_MemAlloc((margc + 1) * (sizeof(char *)));
{
int i = 0;
StdArg *stdargs = JLI_GetStdArgs();
for (i = 0 ; i < margc ; i++) {
margv[i] = stdargs[i].arg;
}
margv[i] = NULL;
}
#else /* *NIXES */
margc = argc;
margv = argv;
#endif /* WIN32 */
//调用JLI_Launch执行java的执行体逻辑,参考代码jdk8u-dev/jdk/src/share/bin/java.c
return JLI_Launch(margc, //命令参数个数
margv, //参数数组
sizeof(const_jargs) / sizeof(char *), //java args参数个数
const_jargs, //java参数
sizeof(const_appclasspath) / sizeof(char *), //classpath 数量
const_appclasspath, //classpath数量
FULL_VERSION, //完整的版本号
DOT_VERSION, //版本号
(const_progname != NULL) ? const_progname : *margv, //程序名称
(const_launcher != NULL) ? const_launcher : *margv, //启动器名称
(const_jargs != NULL) ? JNI_TRUE : JNI_FALSE, //默认为0
const_cpwildcard, //是否支持扩展classpath 默认为true
const_javaw, //window下true, 其他false
const_ergo_class);//运行模式 默认为DEFAULT_POLICY=0 其他包括NEVER_SERVER_CLASS、ALWAYS_SERVER_CLASS
}
JLI_Launch
作用:java入口函数,解析参数、创建环境、加载jvm动态库
位置:jdk8u-dev/jdk/src/share/bin/java.c
/*
* Entry point. java的入口函数
*/
int
JLI_Launch(int argc, char ** argv, /* main argc, argc */
int jargc, const char** jargv, /* java args */
int appclassc, const char** appclassv, /* app classpath */
const char* fullversion, /* full version defined */
const char* dotversion, /* dot version defined */
const char* pname, /* program name */
const char* lname, /* launcher name */
jboolean javaargs, /* JAVA_ARGS */
jboolean cpwildcard, /* classpath wildcard*/
jboolean javaw, /* windows-only javaw */
jint ergo /* ergonomics class policy */
)
{
int mode = LM_UNKNOWN; //启动模式 默认为0 其他参考:java.h 中LaunchMode枚举定义
char *what = NULL;
char *cpath = 0;
char *main_class = NULL; //带有main函数的class文件
int ret;
InvocationFunctions ifn; //函数指针结构体包括:创建jvm、获取jvm启动参数、获取创建的jvm
jlong start, end; //启动结束时间
char jvmpath[MAXPATHLEN]; //jvm路径
char jrepath[MAXPATHLEN]; //jre路径
char jvmcfg[MAXPATHLEN]; //jvm配置
_fVersion = fullversion;
_dVersion = dotversion;
_launcher_name = lname;
_program_name = pname;
_is_java_args = javaargs;
_wc_enabled = cpwildcard;
_ergo_policy = ergo;
//初始化启动器,window下注册启动异常时弹出ui选择调试器
InitLauncher(javaw);
DumpState(); //判断是否定义环境变量_JAVA_LAUNCHER_DEBUG,是的话打印调试信息
if (JLI_IsTraceLauncher()) { //打印命令行参数
int i;
printf("Command line args:\n");
for (i = 0; i < argc ; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
//增加配置参数
AddOption("-Dsun.java.launcher.diag=true", NULL);
}
/*
* There are three things to note about the SelectVersion() routine:
* 1) If the version running isn't correct, this routine doesn't
* return (either the correct version has been exec'd or an error
* was issued).
* 2) Argc and Argv in this scope are *not* altered by this routine.
* It is the responsibility of subsequent code to ignore the
* arguments handled by this routine.
* 3) As a side-effect, the variable "main_class" is guaranteed to
* be set (if it should ever be set). This isn't exactly the
* poster child for structured programming, but it is a small
* price to pay for not processing a jar file operand twice.
* (Note: This side effect has been disabled. See comment on
* bugid 5030265 below.)
*/
//选择合适版本的jre,可以从jar包中读取META-INF/MAINFEST.MF中获取
//查看mian_class
SelectVersion(argc, argv, &main_class);
//创建执行环境
CreateExecutionEnvironment(&argc,
&argv,
jrepath,
sizeof(jrepath),
jvmpath,
sizeof(jvmpath),
jvmcfg,
sizeof(jvmcfg));
//初始设置函数指针为无效指针
ifn.CreateJavaVM = 0;
ifn.GetDefaultJavaVMInitArgs = 0;
//如果时debug的话,记录启动时间
if (JLI_IsTraceLauncher()) {
start = CounterGet();
}
//加载java vm虚拟机
//加载jvmpath(jdk/lib/amd64/server/libjvm.so),并初始化jvm的指针
/**
* 获取动态库中的JNI_CreateJavaVM、JNI_GetDefaultJavaVMInitArgs、JNI_GetCreatedJavaVMs入口地址
* 填充到ifn结构体中
*/
if (!LoadJavaVM(jvmpath, &ifn)) {
return(6);
}
//debug模式下记录加载java vm虚拟机时间
if (JLI_IsTraceLauncher()) {
end = CounterGet();
}
//打印加载javavm时间
JLI_TraceLauncher("%ld micro seconds to LoadJavaVM\n",
(long)(jint)Counter2Micros(end-start));
++argv;
--argc;
//是否有参数
if (IsJavaArgs()) {
/* 解析参数 */
TranslateApplicationArgs(jargc, jargv, &argc, &argv);
if (!AddApplicationOptions(appclassc, appclassv)) {
return(1);
}
} else {
/* 设置环境变量中的classpath路径 */
cpath = getenv("CLASSPATH");
if (cpath == NULL) {
cpath = ".";
}
SetClassPath(cpath);
}
/* 解析命令行参数,如果解析失败则程序退出.
*/
if (!ParseArguments(&argc, &argv, &mode, &what, &ret, jrepath)) {
return(ret);
}
/* 如果-jar参数指定的话,重写classpath值 */
if (mode == LM_JAR) {
SetClassPath(what); /* Override class path */
}
/* set the -Dsun.java.command pseudo property */
SetJavaCommandLineProp(what, argc, argv);
/* Set the -Dsun.java.launcher pseudo property */
SetJavaLauncherProp();
/* set the -Dsun.java.launcher.* platform properties */
SetJavaLauncherPlatformProps();
//进行一系列处理,最终创建一个jvm的虚拟机并条用int JNICALL JavaMain(void * _args);运行入口函数
return JVMInit(&ifn, threadStackSize, argc, argv, mode, what, ret);
}
JVMInit
作用:显示启动画面,执行虚拟机入口函数
位置:jdk8u-dev/jdk/src/solaris/bin/java_md_solinux.c
int JVMInit(InvocationFunctions* ifn, //函数指针
jlong threadStackSize, //线程栈大小 在AddOption函数中修改全局的threadStackSize变量值,通过-Xss修改
int argc, //参数数目
char **argv, //参数
int mode, //模式
char *what, //
int ret) //返回值
{
//根据存储在环境变量中的jar文件名和镜像文件名显示启动画面
ShowSplashScreen();
//创建线程执行代码逻辑
return ContinueInNewThread(ifn, threadStackSize, argc, argv, mode, what, ret);
}
ContinueInNewThread
作用:组织参数,执行真正的虚拟机入口函数
位置:jdk8u-dev/jdk/src/share/bin/java.c
int ContinueInNewThread(InvocationFunctions* ifn, //函数指针
jlong threadStackSize, //线程栈大小
int argc,
char **argv,
int mode,
char *what,
int ret)
{
/*
* 如果没有指定线程栈大小, 则会检查vm是否指定一个默认值.
* 注意:虚拟机不再支持1.1,但是他会通过初始化参数返回一个默认的栈大小值.
*/
if (threadStackSize == 0) {
struct JDK1_1InitArgs args1_1;
memset((void*)&args1_1, 0, sizeof(args1_1));
//指定版本号
args1_1.version = JNI_VERSION_1_1;
//通过虚拟机返回一个指定的参数值,该方法时从libjvm.so里面导出的函数指针
ifn->GetDefaultJavaVMInitArgs(&args1_1); /* 忽略返回值 */
if (args1_1.javaStackSize > 0) {
//如果查询到有效值,则会修改全局定义的栈大小
threadStackSize = args1_1.javaStackSize;
}
}
{
/* 创建一个新线程去创建jvm,然后调用main方法*/
JavaMainArgs args;
int rslt;
args.argc = argc;
args.argv = argv;
args.mode = mode;
args.what = what;
args.ifn = *ifn;
/**/
rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args);
/* If the caller has deemed there is an error we
* simply return that, otherwise we return the value of
* the callee
*/
return (ret != 0) ? ret : rslt;
}
}
ContinueInNewThread0
作用:尝试创建新线程执行代码逻辑,创建新线程失败则在当前线程执行代码逻辑
位置:jdk8u-dev/jdk/src/solaris/bin/java_md_solinux.c
/*
* 暂停当前线程,然后继续执行一个新的线程
*/
int
ContinueInNewThread0(int (JNICALL *continuation)(void *), //线程入口函数,在这里具体执行的时JavaMain方法
jlong stack_size, //线程栈大小,会使用该值创建线程
void * args) { //参数
int rslt;
#ifdef __linux__ //linux代码块
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (stack_size > 0) {
pthread_attr_setstacksize(&attr, stack_size); //如果给定的线程栈大小值有效,则设置创建线程的栈大小,否则使用默认值
}
/* 调用线程库创建线程,并设定入口函数为JavaMain */
if (pthread_create(&tid, &attr, (void *(*)(void*))continuation, (void*)args) == 0) {
void * tmp;
pthread_join(tid, &tmp); //创建成功后,并等待线程的结束,主线程挂起
rslt = (int)tmp;
} else {
/*
* 如果因为某些条件导致创建线程失败,则在当前线程执行JavaMain方法
*/
rslt = continuation(args);
}
pthread_attr_destroy(&attr);
#else /* ! __linux__ */ //solaris系统
thread_t tid;
long flags = 0;
if (thr_create(NULL, stack_size, (void *(*)(void *))continuation, args, flags, &tid) == 0) {
void * tmp;
thr_join(tid, NULL, &tmp);
rslt = (int)tmp;
} else {
/* See above. Continue in current thread if thr_create() failed */
rslt = continuation(args);
}
#endif /* __linux__ */
return rslt;
}
JavaMain
作用:虚拟机的入口函数
位置:jdk8u-dev/jdk/src/share/bin/java.c
int JNICALL
JavaMain(void * _args)
{
JavaMainArgs *args = (JavaMainArgs *)_args; //获取参数
int argc = args->argc;
char **argv = args->argv;
int mode = args->mode;
char *what = args->what;
InvocationFunctions ifn = args->ifn; //当前虚拟机导致的函数指针
//该机制可以保证同一环境配置多个jdk版本
JavaVM *vm = 0;
JNIEnv *env = 0;
jclass mainClass = NULL; //main函数class
jclass appClass = NULL; // 正在启动的实际应用程序类
jmethodID mainID;
jobjectArray mainArgs;
int ret = 0;
jlong start, end;
RegisterThread(); //window/类unix为空实现,macos特别处理
/* 初始化虚拟机 */
start = CounterGet();
if (!InitializeJVM(&vm, &env, &ifn)) { //初始化虚拟机,通过调用libvm.so
//导出的CreateJavaVM执行真正的
//创建虚拟机逻辑,最主要的时把env绑定到具体的jvm上
//后续的操作都是通过JNIEnv实现的
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
if (showSettings != NULL) {
ShowSettings(env, showSettings);
CHECK_EXCEPTION_LEAVE(1);
}
if (printVersion || showVersion) {
PrintJavaVersion(env, showVersion);
CHECK_EXCEPTION_LEAVE(0);
if (printVersion) {
LEAVE();
}
}
/* 如果没有指定class或者jar,则直接退出 */
if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
PrintUsage(env, printXUsage);
CHECK_EXCEPTION_LEAVE(1);
LEAVE();
}
FreeKnownVMs(); /* after last possible PrintUsage() */
/* 记录初始化jvm时间 */
if (JLI_IsTraceLauncher()) {
end = CounterGet();
JLI_TraceLauncher("%ld micro seconds to InitializeJVM\n",
(long)(jint)Counter2Micros(end-start));
}
/* 接下来,从argv获取应用的参数,打印参数 */
if (JLI_IsTraceLauncher()){
int i;
printf("%s is '%s'\n", launchModeNames[mode], what);
printf("App's argc is %d\n", argc);
for (i=0; i < argc; i++) {
printf(" argv[%2d] = '%s'\n", i, argv[i]);
}
}
ret = 1;
/*
* Get the application's main class.
*
* See bugid 5030265. The Main-Class name has already been parsed
* from the manifest, but not parsed properly for UTF-8 support.
* Hence the code here ignores the value previously extracted and
* uses the pre-existing code to reextract the value. This is
* possibly an end of release cycle expedient. However, it has
* also been discovered that passing some character sets through
* the environment has "strange" behavior on some variants of
* Windows. Hence, maybe the manifest parsing code local to the
* launcher should never be enhanced.
*
* Hence, future work should either:
* 1) Correct the local parsing code and verify that the
* Main-Class attribute gets properly passed through
* all environments,
* 2) Remove the vestages of maintaining main_class through
* the environment (and remove these comments).
*
* This method also correctly handles launching existing JavaFX
* applications that may or may not have a Main-Class manifest entry.
*/
/* 加载Main class */
mainClass = LoadMainClass(env, mode, what);
/* 检查是否指定main class, 不存在则退出虚拟机*/
CHECK_EXCEPTION_NULL_LEAVE(mainClass);
/*
* In some cases when launching an application that needs a helper, e.g., a
* JavaFX application with no main method, the mainClass will not be the
* applications own main class but rather a helper class. To keep things
* consistent in the UI we need to track and report the application main class.
*/
/* 获取应用的class文件 */
appClass = GetApplicationClass(env);
/* 检查是否指定app class, 不存在返回-1 */
NULL_CHECK_RETURN_VALUE(appClass, -1);
/*
* PostJVMInit uses the class name as the application name for GUI purposes,
* for example, on OSX this sets the application name in the menu bar for
* both SWT and JavaFX. So we'll pass the actual application class here
* instead of mainClass as that may be a launcher or helper class instead
* of the application class.
*/
/*window和类unix为空实现,在macos下设定gui程序的程序名称*/
PostJVMInit(env, appClass, vm);
/*
* The LoadMainClass not only loads the main class, it will also ensure
* that the main method's signature is correct, therefore further checking
* is not required. The main method is invoked here so that extraneous java
* stacks are not in the application stack trace.
*/
/*从mainclass中加载静态main方法*/
mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
"([Ljava/lang/String;)V");
CHECK_EXCEPTION_NULL_LEAVE(mainID);
/* Build platform specific argument array */
mainArgs = CreateApplicationArgs(env, argv, argc);
CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
/* Invoke main method. */
/* 调用main方法,并把参数传递过去 */
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
/*
* The launcher's exit code (in the absence of calls to
* System.exit) will be non-zero if main threw an exception.
*/
ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
LEAVE();
}
InitializeJVM
作用:通过libvm.so导出的函数创建虚拟机并和JNIEnv绑定
位置:jdk8u-dev/jdk/src/share/bin/java.c
/*
* 初始化java虚拟机
*/
static jboolean
InitializeJVM(JavaVM **pvm, //虚拟机指针
JNIEnv **penv, //JNIEnv指针
InvocationFunctions *ifn) //导出的函数指针
{
JavaVMInitArgs args;
jint r;
memset(&args, 0, sizeof(args));
args.version = JNI_VERSION_1_2; //指定jni版本号
args.nOptions = numOptions;
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
if (JLI_IsTraceLauncher()) { //打印参数
int i = 0;
printf("JavaVM args:\n ");
printf("version 0x%08lx, ", (long)args.version);
printf("ignoreUnrecognized is %s, ",
args.ignoreUnrecognized ? "JNI_TRUE" : "JNI_FALSE");
printf("nOptions is %ld\n", (long)args.nOptions);
for (i = 0; i < numOptions; i++)
printf(" option[%2d] = '%s'\n",
i, args.options[i].optionString);
}
/* 创建java虚拟机,并和JNIEnv绑定 */
r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
/* 释放参数内存 */
JLI_MemFree(options);
/* 返回创建结果 */
return r == JNI_OK;
}
JNI_CreateJavaVM
作用:创建java虚拟机
位置:jdk8u-dev/hotspot/src/share/vm/prims/jni.cpp
/* 导出JNI_CreateJavaVM函数 */
_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {
#ifndef USDT2
HS_DTRACE_PROBE3(hotspot_jni, CreateJavaVM__entry, vm, penv, args);
#else /* USDT2 */
HOTSPOT_JNI_CREATEJAVAVM_ENTRY(
(void **) vm, penv, args);
#endif /* USDT2 */
jint result = JNI_ERR;
DT_RETURN_MARK(CreateJavaVM, jint, (const jint&)result);
// We're about to use Atomic::xchg for synchronization. Some Zero
// platforms use the GCC builtin __sync_lock_test_and_set for this,
// but __sync_lock_test_and_set is not guaranteed to do what we want
// on all architectures. So we check it works before relying on it.
#if defined(ZERO) && defined(ASSERT)
{
jint a = 0xcafebabe;
jint b = Atomic::xchg(0xdeadbeef, &a);
void *c = &a;
void *d = Atomic::xchg_ptr(&b, &c);
assert(a == (jint) 0xdeadbeef && b == (jint) 0xcafebabe, "Atomic::xchg() works");
assert(c == &b && d == &a, "Atomic::xchg_ptr() works");
}
#endif // ZERO && ASSERT
// At the moment it's only possible to have one Java VM,
// since some of the runtime state is in global variables.
// We cannot use our mutex locks here, since they only work on
// Threads. We do an atomic compare and exchange to ensure only
// one thread can call this method at a time
// We use Atomic::xchg rather than Atomic::add/dec since on some platforms
// the add/dec implementations are dependent on whether we are running
// on a multiprocessor, and at this stage of initialization the os::is_MP
// function used to determine this will always return false. Atomic::xchg
// does not have this problem.
if (Atomic::xchg(1, &vm_created) == 1) {
return JNI_EEXIST; // already created, or create attempt in progress
}
if (Atomic::xchg(0, &safe_to_recreate_vm) == 0) {
return JNI_ERR; // someone tried and failed and retry not allowed.
}
assert(vm_created == 1, "vm_created is true during the creation");
/**
* 初始化期间的某些错误是可恢复的,并且不会阻止稍后再次调用此方法(可能使用不同的参数)。
* 但是,在初始化期间的某个时刻如果发生错误,我们不能再次调用此函数(否则它将崩溃)。
* 在这些情况下,ccan_try_again标志设置为false,它将 safe_to_recreate_vm 原子地设置为1,
* 这样任何对JNI_CreateJavaVM的新调用都将立即使用上述逻辑失败。
*/
bool can_try_again = true;
/* 创建java虚拟机 */
result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
if (result == JNI_OK) {
JavaThread *thread = JavaThread::current();
/* thread is thread_in_vm here */
/* 获取vm对象,从全局的main_vm赋值 */
*vm = (JavaVM *)(&main_vm);
/* 获取线程jni环境指针 */
*(JNIEnv**) penv = thread->jni_environment();
// Tracks the time application was running before GC
// 记录程序启动时间,跟从应用程序在gc前的运行时间
RuntimeService::record_application_start();
// Notify JVMTI
//
if (JvmtiExport::should_post_thread_life()) {
JvmtiExport::post_thread_start(thread);
}
EventThreadStart event;
if (event.should_commit()) {
event.set_javalangthread(java_lang_Thread::thread_id(thread->threadObj()));
event.commit();
}
#ifndef PRODUCT
#ifndef TARGET_OS_FAMILY_windows
#define CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(f) f()
#endif
// Check if we should compile all classes on bootclasspath
if (CompileTheWorld) ClassLoader::compile_the_world();
if (ReplayCompiles) ciReplay::replay(thread);
// Some platforms (like Win*) need a wrapper around these test
// functions in order to properly handle error conditions.
CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(test_error_handler);
CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(execute_internal_vm_tests);
#endif
// Since this is not a JVM_ENTRY we have to set the thread state manually before leaving.
ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native);
} else {
/* 创建失败,如果允许重试,则修改safe_to_recreate_vm标志 */
if (can_try_again) {
// reset safe_to_recreate_vm to 1 so that retrial would be possible
safe_to_recreate_vm = 1;
}
// Creation failed. We must reset vm_created
// 创建失败,需要重新设置vm_create值
*vm = 0;
*(JNIEnv**)penv = 0;
// reset vm_created last to avoid race condition. Use OrderAccess to
// control both compiler and architectural-based reordering.
OrderAccess::release_store(&vm_created, 0);
}
return result;
}
Threads::create_vm
作用:hotspot创建java虚拟机函数
位置:jdk8u-dev/hotspot/src/share/vm/runtime/thread.cpp
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
//调用JDK_Version_init();
//加载libjava.so、libverify.so库,通过调用库中导出的JDK_GetVersionInfo0查询当前虚拟机版本
extern void JDK_Version_init();
// 检查当前版本是否在支持范围内,主要不支持1.1版本 高于1.1都返回true
if (!is_supported_jni_version(args->version)) return JNI_EVERSION;
//初始化系统输出流模块
ostream_init();
// 处理java启动属性.
Arguments::process_sun_java_launcher_properties(args);
// Initialize the os module before using TLS
// 初始化系统环境,例如:获取当前的进程pid、获取系统时钟、设置内存页大小
// 获取cpu数、获取物理内存大小
os::init();
// Initialize system properties.
// 设置系统属性, key-value
// 设置了java虚拟机信息、清空由os::init_system_properties_values()方法设置的值
Arguments::init_system_properties();
// So that JDK version can be used as a discrimintor when parsing arguments
// 获取jdk版本号,作为接下来参数解析的依据
JDK_Version_init();
// Update/Initialize System properties after JDK version number is known
// 设定jdk版本后再去更新系统版本、供应商属性值,此处主要设定3个参数
// jdk1.7以后的版本厂商修改为oracle,之前为sun
// 1、java.vm.specification.vendor 可选值为:Oracle Corporation / Sun Microsystems Inc.
// 2、java.vm.specification.version 可选值为:1.0 / 1.7 1.8 1.9 etc
// 3、java.vm.vendor 可选值为: Oracle Corporation / Sun Microsystems Inc.
Arguments::init_version_specific_system_properties();
// Parse arguments
// 解析参数,生成java虚拟机运行期间的一些参数指标
// -XX:Flags= -XX:+PrintVMOptions -XX:-PrintVMOptions -XX:+IgnoreUnrecognizedVMOptions
// -XX:-IgnoreUnrecognizedVMOptions -XX:+PrintFlagsInitial -XX:NativeMemoryTracking
jint parse_result = Arguments::parse(args);
if (parse_result != JNI_OK) return parse_result;
//主要完成大页支持以及linux上的coredump_filter配置
os::init_before_ergo();
//调整运行环境得参数及一些指标
//1、设置参数及指标 如果是server模式并且没有指定gc策略,则默认使用UsePaallelGC
// 64位下启用普通对象压缩
//2、设置是否使用共享存储空间(开启对象压缩、指针压缩后关闭)
//3、检查gc一致性
//4、依据物理内存设置对内存大小
//5、设置各种gc的参数标志:parallel、cms、g1、panew
//6、初始化jvm的平衡因子参数
//7、设定字节码重写标志
//8、如果指定-XX:+AggressiveOpts参数,则设定加快编译,依赖于EliminateAutoBox及DoEscapeAnalysis标志位及C2下
//9、根据是否指定gama lanucher及是否是调试状态设定暂停状态位
jint ergo_result = Arguments::apply_ergo();
if (ergo_result != JNI_OK) return ergo_result;
//暂停
if (PauseAtStartup) {
os::pause();
}
#ifndef USDT2
HS_DTRACE_PROBE(hotspot, vm__init__begin);
#else /* USDT2 */
HOTSPOT_VM_INIT_BEGIN();
#endif /* USDT2 */
// Record VM creation timing statistics
//记录jvm启动开始时间
TraceVmCreationTime create_vm_timer;
create_vm_timer.start();
// Timing (must come after argument parsing)
TraceTime timer("Create VM", TraceStartupTime);
// Initialize the os module after parsing the args
//解析参数后,初始化系统模块
//1、如果使用linux下的posix线程库cpu时钟的话,使用pthread_getcpuclockid方法获取jvm线程
// 的cpu时钟id,然后基于这个线程的时钟进行时间统计计数
//2、分配一个linux内存单页并标记位为可循环读的安全入口点
//3、初始化暂停/恢复运行的支持,主要通过注册信号处理程序支持该功能
//4、注册信号处理程序
//5、安装信号处理程序
//6、计算线程栈大小
//7、设置glibc、linux线程版本信息
//8、设置linux系统进程文件描述符值
//9、创建用于线程创建的线程锁
//11、初始化线程的优先级
jint os_init_2_result = os::init_2();
if (os_init_2_result != JNI_OK) return os_init_2_result;
//调整参数,主要针对numa架构下ParallelGC、OarallelOldGc调整堆内存参数
jint adjust_after_os_result = Arguments::adjust_after_os();
if (adjust_after_os_result != JNI_OK) return adjust_after_os_result;
// intialize TLS
// 初始化线程本地存储,通过ptread_create_key创建一个线程特有的key_t,
// 后面通过pthread_setspecific\pthread_getspecific设置、获取线程私有数据
ThreadLocalStorage::init();
// Bootstrap native memory tracking, so it can start recording memory
// activities before worker thread is started. This is the first phase
// of bootstrapping, VM is currently running in single-thread mode.
// 启动本地内存跟踪,因此它可以在工作线程启动之前开始记录内存活动。
// 这是引导的第一阶段,JVM当前以单线程模式运行。
MemTracker::bootstrap_single_thread();
// Initialize output stream logging
// 初始化jvm的日志输出流,如果指定-Xloggc:logfilepath则根据参数指定生成输出流到文件
ostream_init_log();
// Convert -Xrun to -agentlib: if there is no JVM_OnLoad
// Must be before create_vm_init_agents()
// 如果指定了-Xrun参数,如:hprof性能分析、jdwp远程调试
if (Arguments::init_libraries_at_startup()) {
convert_vm_init_libraries_to_agents();
}
// Launch -agentlib/-agentpath and converted -Xrun agents
if (Arguments::init_agents_at_startup()) {
create_vm_init_agents();
}
// Initialize Threads state
_thread_list = NULL;
_number_of_threads = 0;
_number_of_non_daemon_threads = 0;
// Initialize global data structures and create system classes in heap
// 1、初始化全局结构体
// 2、在内存中创建jvm体系的class文件
// 3、初始化事件记录日志对象
// 4、初始化全局资源锁mutex
// 5、内存池初始化large_pool、medium_pool、small_poll、tiny_pool
// 6、启用该perfdata功能。此选项默认启用以允许JVM监视和性能测试,如果指定 -XX:+UsePerfData
vm_init_globals();
// Attach the main thread to this os thread
// 创建新的主java线程, 设置主线程状态为运行在jvm里面
JavaThread* main_thread = new JavaThread();
main_thread->set_thread_state(_thread_in_vm);
// must do this before set_active_handles and initialize_thread_local_storage
// Note: on solaris initialize_thread_local_storage() will (indirectly)
// change the stack size recorded here to one based on the java thread
// stacksize. This adjusted size is what is used to figure the placement
// of the guard pages.
// 记录栈基址、栈大小值
main_thread->record_stack_base_and_size();
// 初始化主线程的本地存储
main_thread->initialize_thread_local_storage();
// 绑定jniHandleBlock指针, 处理jni逻辑句柄
main_thread->set_active_handles(JNIHandleBlock::allocate_block());
// 设定main_thread为主线程
if (!main_thread->set_as_starting_thread()) {
vm_shutdown_during_initialization(
"Failed necessary internal allocation. Out of swap space");
delete main_thread;
*canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
return JNI_ENOMEM;
}
// Enable guard page *after* os::create_main_thread(), otherwise it would
// crash Linux VM, see notes in os_linux.cpp.
main_thread->create_stack_guard_pages();
// Initialize Java-Level synchronization subsystem
ObjectMonitor::Initialize() ;
// Second phase of bootstrapping, VM is about entering multi-thread mode
MemTracker::bootstrap_multi_thread();
// Initialize global modules
// 初始化全局模块
//
jint status = init_globals();
if (status != JNI_OK) {
delete main_thread;
*canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
return status;
}
// Should be done after the heap is fully created
main_thread->cache_global_variables();
HandleMark hm;
{ MutexLocker mu(Threads_lock);
Threads::add(main_thread);
}
// Any JVMTI raw monitors entered in onload will transition into
// real raw monitor. VM is setup enough here for raw monitor enter.
JvmtiExport::transition_pending_onload_raw_monitors();
// Fully start NMT
MemTracker::start();
// Create the VMThread
{ TraceTime timer("Start VMThread", TraceStartupTime);
VMThread::create();
Thread* vmthread = VMThread::vm_thread();
if (!os::create_thread(vmthread, os::vm_thread))
vm_exit_during_initialization("Cannot create VM thread. Out of system resources.");
// Wait for the VM thread to become ready, and VMThread::run to initialize
// Monitors can have spurious returns, must always check another state flag
{
MutexLocker ml(Notify_lock);
os::start_thread(vmthread);
while (vmthread->active_handles() == NULL) {
Notify_lock->wait();
}
}
}
assert (Universe::is_fully_initialized(), "not initialized");
if (VerifyDuringStartup) {
// Make sure we're starting with a clean slate.
VM_Verify verify_op;
VMThread::execute(&verify_op);
}
EXCEPTION_MARK;
// At this point, the Universe is initialized, but we have not executed
// any byte code. Now is a good time (the only time) to dump out the
// internal state of the JVM for sharing.
if (DumpSharedSpaces) {
MetaspaceShared::preload_and_dump(CHECK_0);
ShouldNotReachHere();
}
// Always call even when there are not JVMTI environments yet, since environments
// may be attached late and JVMTI must track phases of VM execution
JvmtiExport::enter_start_phase();
// Notify JVMTI agents that VM has started (JNI is up) - nop if no agents.
JvmtiExport::post_vm_start();
{
TraceTime timer("Initialize java.lang classes", TraceStartupTime);
if (EagerXrunInit && Arguments::init_libraries_at_startup()) {
create_vm_init_libraries();
}
initialize_class(vmSymbols::java_lang_String(), CHECK_0);
// Initialize java_lang.System (needed before creating the thread)
initialize_class(vmSymbols::java_lang_System(), CHECK_0);
initialize_class(vmSymbols::java_lang_ThreadGroup(), CHECK_0);
Handle thread_group = create_initial_thread_group(CHECK_0);
Universe::set_main_thread_group(thread_group());
initialize_class(vmSymbols::java_lang_Thread(), CHECK_0);
oop thread_object = create_initial_thread(thread_group, main_thread, CHECK_0);
main_thread->set_threadObj(thread_object);
// Set thread status to running since main thread has
// been started and running.
java_lang_Thread::set_thread_status(thread_object,
java_lang_Thread::RUNNABLE);
// The VM creates & returns objects of this class. Make sure it's initialized.
initialize_class(vmSymbols::java_lang_Class(), CHECK_0);
// The VM preresolves methods to these classes. Make sure that they get initialized
initialize_class(vmSymbols::java_lang_reflect_Method(), CHECK_0);
initialize_class(vmSymbols::java_lang_ref_Finalizer(), CHECK_0);
call_initializeSystemClass(CHECK_0);
// get the Java runtime name after java.lang.System is initialized
JDK_Version::set_runtime_name(get_java_runtime_name(THREAD));
JDK_Version::set_runtime_version(get_java_runtime_version(THREAD));
// an instance of OutOfMemory exception has been allocated earlier
initialize_class(vmSymbols::java_lang_OutOfMemoryError(), CHECK_0);
initialize_class(vmSymbols::java_lang_NullPointerException(), CHECK_0);
initialize_class(vmSymbols::java_lang_ClassCastException(), CHECK_0);
initialize_class(vmSymbols::java_lang_ArrayStoreException(), CHECK_0);
initialize_class(vmSymbols::java_lang_ArithmeticException(), CHECK_0);
initialize_class(vmSymbols::java_lang_StackOverflowError(), CHECK_0);
initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK_0);
initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK_0);
}
// See : bugid 4211085.
// Background : the static initializer of java.lang.Compiler tries to read
// property"java.compiler" and read & write property "java.vm.info".
// When a security manager is installed through the command line
// option "-Djava.security.manager", the above properties are not
// readable and the static initializer for java.lang.Compiler fails
// resulting in a NoClassDefFoundError. This can happen in any
// user code which calls methods in java.lang.Compiler.
// Hack : the hack is to pre-load and initialize this class, so that only
// system domains are on the stack when the properties are read.
// Currently even the AWT code has calls to methods in java.lang.Compiler.
// On the classic VM, java.lang.Compiler is loaded very early to load the JIT.
// Future Fix : the best fix is to grant everyone permissions to read "java.compiler" and
// read and write"java.vm.info" in the default policy file. See bugid 4211383
// Once that is done, we should remove this hack.
initialize_class(vmSymbols::java_lang_Compiler(), CHECK_0);
// More hackery - the static initializer of java.lang.Compiler adds the string "nojit" to
// the java.vm.info property if no jit gets loaded through java.lang.Compiler (the hotspot
// compiler does not get loaded through java.lang.Compiler). "java -version" with the
// hotspot vm says "nojit" all the time which is confusing. So, we reset it here.
// This should also be taken out as soon as 4211383 gets fixed.
reset_vm_info_property(CHECK_0);
quicken_jni_functions();
// Must be run after init_ft which initializes ft_enabled
if (TRACE_INITIALIZE() != JNI_OK) {
vm_exit_during_initialization("Failed to initialize tracing backend");
}
// Set flag that basic initialization has completed. Used by exceptions and various
// debug stuff, that does not work until all basic classes have been initialized.
set_init_completed();
#ifndef USDT2
HS_DTRACE_PROBE(hotspot, vm__init__end);
#else /* USDT2 */
HOTSPOT_VM_INIT_END();
#endif /* USDT2 */
// record VM initialization completion time
#if INCLUDE_MANAGEMENT
Management::record_vm_init_completed();
#endif // INCLUDE_MANAGEMENT
// Compute system loader. Note that this has to occur after set_init_completed, since
// valid exceptions may be thrown in the process.
// Note that we do not use CHECK_0 here since we are inside an EXCEPTION_MARK and
// set_init_completed has just been called, causing exceptions not to be shortcut
// anymore. We call vm_exit_during_initialization directly instead.
SystemDictionary::compute_java_system_loader(THREAD);
if (HAS_PENDING_EXCEPTION) {
vm_exit_during_initialization(Handle(THREAD, PENDING_EXCEPTION));
}
#if INCLUDE_ALL_GCS
// Support for ConcurrentMarkSweep. This should be cleaned up
// and better encapsulated. The ugly nested if test would go away
// once things are properly refactored. XXX YSR
if (UseConcMarkSweepGC || UseG1GC) {
if (UseConcMarkSweepGC) {
ConcurrentMarkSweepThread::makeSurrogateLockerThread(THREAD);
} else {
ConcurrentMarkThread::makeSurrogateLockerThread(THREAD);
}
if (HAS_PENDING_EXCEPTION) {
vm_exit_during_initialization(Handle(THREAD, PENDING_EXCEPTION));
}
}
#endif // INCLUDE_ALL_GCS
// Always call even when there are not JVMTI environments yet, since environments
// may be attached late and JVMTI must track phases of VM execution
JvmtiExport::enter_live_phase();
// Signal Dispatcher needs to be started before VMInit event is posted
os::signal_init();
// Start Attach Listener if +StartAttachListener or it can't be started lazily
if (!DisableAttachMechanism) {
AttachListener::vm_start();
if (StartAttachListener || AttachListener::init_at_startup()) {
AttachListener::init();
}
}
// Launch -Xrun agents
// Must be done in the JVMTI live phase so that for backward compatibility the JDWP
// back-end can launch with -Xdebug -Xrunjdwp.
if (!EagerXrunInit && Arguments::init_libraries_at_startup()) {
create_vm_init_libraries();
}
// Notify JVMTI agents that VM initialization is complete - nop if no agents.
JvmtiExport::post_vm_initialized();
if (TRACE_START() != JNI_OK) {
vm_exit_during_initialization("Failed to start tracing backend.");
}
if (CleanChunkPoolAsync) {
Chunk::start_chunk_pool_cleaner_task();
}
// initialize compiler(s)
#if defined(COMPILER1) || defined(COMPILER2) || defined(SHARK)
CompileBroker::compilation_init();
#endif
if (EnableInvokeDynamic) {
// Pre-initialize some JSR292 core classes to avoid deadlock during class loading.
// It is done after compilers are initialized, because otherwise compilations of
// signature polymorphic MH intrinsics can be missed
// (see SystemDictionary::find_method_handle_intrinsic).
initialize_class(vmSymbols::java_lang_invoke_MethodHandle(), CHECK_0);
initialize_class(vmSymbols::java_lang_invoke_MemberName(), CHECK_0);
initialize_class(vmSymbols::java_lang_invoke_MethodHandleNatives(), CHECK_0);
}
#if INCLUDE_MANAGEMENT
Management::initialize(THREAD);
#endif // INCLUDE_MANAGEMENT
if (HAS_PENDING_EXCEPTION) {
// management agent fails to start possibly due to
// configuration problem and is responsible for printing
// stack trace if appropriate. Simply exit VM.
vm_exit(1);
}
if (Arguments::has_profile()) FlatProfiler::engage(main_thread, true);
if (MemProfiling) MemProfiler::engage();
StatSampler::engage();
if (CheckJNICalls) JniPeriodicChecker::engage();
BiasedLocking::init();
if (JDK_Version::current().post_vm_init_hook_enabled()) {
call_postVMInitHook(THREAD);
// The Java side of PostVMInitHook.run must deal with all
// exceptions and provide means of diagnosis.
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
}
}
{
MutexLockerEx ml(PeriodicTask_lock, Mutex::_no_safepoint_check_flag);
// Make sure the watcher thread can be started by WatcherThread::start()
// or by dynamic enrollment.
WatcherThread::make_startable();
// Start up the WatcherThread if there are any periodic tasks
// NOTE: All PeriodicTasks should be registered by now. If they
// aren't, late joiners might appear to start slowly (we might
// take a while to process their first tick).
if (PeriodicTask::num_tasks() > 0) {
WatcherThread::start();
}
}
// Give os specific code one last chance to start
os::init_3();
create_vm_timer.end();
#ifdef ASSERT
_vm_complete = true;
#endif
return JNI_OK;
}
网友评论