经过前文《从JDK源码级深入剖析main方法的运行机制》的分析,我们知道了实现JavaMain方法的四个主要步骤:

  1. 初始化Java虚拟机

  2. 加载主运行类

  3. 通过加载的主运行类,获取main方法

  4. 调用main函数

下面,我们首先来看一下初始化Java虚拟机的具体实现细节。

上代码:

static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn)
{
JavaVMInitArgs args;
jint r;
//使用memset函数将args变量的所有字节都设置为0
memset(&args, 0, sizeof(args));
args.version = JNI_VERSION_1_2;
args.nOptions = numOptions;
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
//如果启用了跟踪启动器,则打印JavaVM参数信息
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虚拟机,CreateJavaVM由LoadJavaVM初始化
r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
JLI_MemFree(options);
return r == JNI_OK;
}

我们来到src\java.base\windows\native\libjli\java_md.c文件中的LoadJavaVM方法实现:

jboolean LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn)
{
HINSTANCE handle;

JLI_TraceLauncher("JVM path is %s\n", jvmpath);

/*
LoadMSVCRT()函数是用来加载MSVCRT库的。
MSVCRT是Microsoft Visual C++ Runtime Library的缩写,
它是在Windows操作系统中提供C/C++运行时库的标准库。
这个库包含了许多常用的函数,如字符串处理、数学计算、文件操作等。
通过加载这个库,程序可以使用这些函数,从而简化代码的编写。
*/
LoadMSVCRT();

/* 加载JVM的DLL文件*/
if ((handle = LoadLibrary(jvmpath)) == 0) {
JLI_ReportErrorMessage(DLL_ERROR4, (char *)jvmpath);
return JNI_FALSE;
}

/* 获取JNI_CreateJavaVM方法地址*/
ifn->CreateJavaVM =
(void *)GetProcAddress(handle, "JNI_CreateJavaVM"); //
/* 获取JVM默认的初始化参数*/
ifn->GetDefaultJavaVMInitArgs =
(void *)GetProcAddress(handle, "JNI_GetDefaultJavaVMInitArgs");
if (ifn->CreateJavaVM == 0 || ifn->GetDefaultJavaVMInitArgs == 0) {
JLI_ReportErrorMessage(JNI_ERROR1, (char *)jvmpath);
return JNI_FALSE;
}

return JNI_TRUE;
}

在src\hotspot\share\prims\jni.cpp中,我们找到了JNI_CreateJavaVM方法的具体实现:

_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {
jint result = JNI_ERR;
// On Windows, let CreateJavaVM run with SEH protection
#if defined(_WIN32) && !defined(USE_VECTORED_EXCEPTION_HANDLING)
__try {
#endif
//创建Java虚拟机的内部实现方法
result = JNI_CreateJavaVM_inner(vm, penv, args);
#if defined(_WIN32) && !defined(USE_VECTORED_EXCEPTION_HANDLING)
} __except(topLevelExceptionFilter((_EXCEPTION_POINTERS*)_exception_info())) {
// Nothing to do.
}
#endif
return result;
}

继续找到JNI_CreateJavaVM_inner方法的实现,我们终于找到了创建Java虚拟机的代码实现:

static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
//下面这些代码是设置创建Java虚拟机相关方法的函数指针
HOTSPOT_JNI_CREATEJAVAVM_ENTRY((void **) vm, penv, args);
jint result = JNI_ERR;
DT_RETURN_MARK(CreateJavaVM, jint, (const jint&)result);
...
//以下两个if代码是确保只有一个线程可以创建Java虚拟机
if (Atomic::xchg(&vm_created, IN_PROGRESS) != NOT_CREATED) {
return JNI_EEXIST; // already created, or create attempt in progress
}
if (Atomic::xchg(&safe_to_recreate_vm, 0) == 0) {
return JNI_ERR;
}
...
//创建Java虚拟机具体实现
result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
//以下代码是将当前线程的Java虚拟机环境与主虚拟机环境绑定,并将创建完成的信息标记为已完成
JavaThread *thread = JavaThread::current();
assert(!thread->has_pending_exception(), "should have returned not OK");
*vm = (JavaVM *)(&main_vm);
*(JNIEnv**)penv = thread->jni_environment();
Atomic::release_store(&vm_created, COMPLETE);
...
return result;
}

以上,通过方法的层层调用,我们找到了创建Java虚拟机具体实现的方法Threads::create_vm((JavaVMInitArgs*) args, &can_try_again),由于里面内容太多,我们放到下一节再细讲。

JVM源码分析:深入剖析java.c文件中JavaMain方法中InitializeJVM的实现的更多相关文章

  1. JVM源码分析之一个Java进程究竟能创建多少线程

    JVM源码分析之一个Java进程究竟能创建多少线程 原创: 寒泉子 你假笨 2016-12-06 概述 虽然这篇文章的标题打着JVM源码分析的旗号,不过本文不仅仅从JVM源码角度来分析,更多的来自于L ...

  2. JVM源码分析之Java对象头实现

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十一篇. 今天呢!灯塔君跟大家讲: JVM源码分析之Java对象头实现 HotSpot虚拟机中,对象在内存中的布局分为三 ...

  3. JVM源码分析之警惕存在内存泄漏风险的FinalReference(增强版)

    概述 JAVA对象引用体系除了强引用之外,出于对性能.可扩展性等方面考虑还特地实现了四种其他引用:SoftReference.WeakReference.PhantomReference.FinalR ...

  4. JVM源码分析之SystemGC完全解读

    JVM源码分析之SystemGC完全解读 概述 JVM的GC一般情况下是JVM本身根据一定的条件触发的,不过我们还是可以做一些人为的触发,比如通过jvmti做强制GC,通过System.gc触发,还可 ...

  5. JVM源码分析之堆外内存完全解读

    JVM源码分析之堆外内存完全解读   寒泉子 2016-01-15 17:26:16 浏览6837 评论0 阿里技术协会 摘要: 概述 广义的堆外内存 说到堆外内存,那大家肯定想到堆内内存,这也是我们 ...

  6. JVM源码分析之Metaspace解密

        概述 metaspace,顾名思义,元数据空间,专门用来存元数据的,它是jdk8里特有的数据结构用来替代perm,这块空间很有自己的特点,前段时间公司这块的问题太多了,主要是因为升级了中间件所 ...

  7. JVM源码分析-JVM源码编译与调试

    要分析JVM的源码,结合资料直接阅读是一种方式,但是遇到一些想不通的场景,必须要结合调试,查看执行路径以及参数具体的值,才能搞得明白.所以我们先来把JVM的源码进行编译,并能够使用GDB进行调试. 编 ...

  8. JVM源码分析-类加载场景实例分析

    A类调用B类的静态方法,除了加载B类,但是B类的一个未被调用的方法间接使用到的C类却也被加载了,这个有意思的场景来自一个提问:方法中使用的类型为何在未调用时尝试加载?. 场景如下: public cl ...

  9. JVM源码分析之堆内存的初始化

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十五篇. ​ 今天呢!灯塔君跟大家讲: JVM源码分析之堆内存的初始化   堆初始化 Java堆的初始化入口位于Univ ...

  10. JVM源码分析之JVM启动流程

      原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十四篇. 今天呢!灯塔君跟大家讲: JVM源码分析之JVM启动流程 前言: 执行Java类的main方法,程序就能运 ...

随机推荐

  1. vue 展开收起的过渡效果

    做的一个项目当中需要做一个组件,传入数组,用v-for生成表单,可以展开和收起,展开收起时需要有过渡的效果 在vue里面提供了<transtion></transtion>和& ...

  2. Django笔记十三之select_for_update等选择和更新等相关操作

    本篇笔记将介绍 update 和 create 的一些其他用法,目录如下: get_or_create update_or_create select_for_update bulk_create b ...

  3. 高尔顿钉板的统计意义—R实现

    提到高尔顿,人们总是把他和钉板实验联系在一起,偶尔也会有人提及他是达尔文的表弟.实际上,作为维多利亚时代的人类学家.统计学家.心理学家和遗传学家,同时又是热带探险家.地理学家.发明家.气象学家,高尔顿 ...

  4. bat基本操作

    一.制作.bat文件:如:新建一个文本文件(.txt)--打开该文件,文件->另存为test.bat,编码为ANSI(支持中文编码): 二.常用写法: 1.输出使用echo:如:echo 文字: ...

  5. pandas之分类操作

    通常情况下,数据集中会存在许多同一类别的信息,比如相同国家.相同行政编码.相同性别等,当这些相同类别的数据多次出现时,就会给数据处理增添许多麻烦,导致数据集变得臃肿,不能直观.清晰地展示数据. 针对上 ...

  6. 清理过时的.NET SDK 和 运行时

    TL;DR .NET 卸载工具 随着这几年.NET 的快速发展,电脑上的.NET SDK 和 runtime 也在不断的积累.每个sdk动辄都是上百兆的空间占用,很快本来就不富裕的C盘空间就更加紧俏了 ...

  7. LeeCode数组问题(二)

    LeeCode 977:有序数组的平方 题目描述: 给你一个按非递减顺序排列的整数数组nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序. 标签:数组,首尾指针,最大值优先 时间复杂度: ...

  8. day48:django前戏:HTTP协议&自定义web框架

    目录 1.HTTP协议 1.HTTP协议简介 2.HTTP协议概述 3.HTTP协议工作原理 4.HTTP协议请求方法 5.HTTP协议状态码 6.URL 7.HTTP请求格式 8.HTTP响应格式 ...

  9. 浅谈对属性描述符__get__、__set__、__delete__的理解

    1.属性描述符的基础介绍 1.1 何为属性描述符? 属性描述符是一种Python语言中的特殊对象,用于定义和控制类属性的行为.属性描述符可以通过定义__get__.__set__.__delete__ ...

  10. Java 的 SPI 机制

    什么是SPI机制? SPI机制( Service Provider Interface)是Java的一种服务发现机制,为了方便应用扩展.那什么是服务发现机制?简单来说,就是你定义了一个接口,但是不提供 ...