最近在看Java并发编程实践和Inside JVM两本书,发现如果不真正的了解底层运作,那么永远是雾里看花。因此从http://openjdk.java.net/groups/hotspot/上下载了源代码,准备研究一番。要想完全研究懂我觉得得对计算机体系结构,C,C++编程,Linux内核都有比较深入的理解。由于并非从事JVM开发工作,因此不会研究的那么深入。

入手就从“java 类名”这个Hello World的命令开始吧,简要的看一下JVM是如何运行起来执行main函数的。

这个实现使用的是C代码(位于share/tools/launcher中的java.c),关注一下是如何执行这个命令的。 

过程主要就是按照参数加载虚拟机,然后找到指定类,执行其main方法。

整个C程序是从main函数开始:

1 int
2 main(int argc, char ** argv)
3 {
4     char *jarfile = 0;
5     char *classname = 0;
6     char *s = 0;
7     char *main_class = NULL;
8     int ret;

设置运行时环境:

   CreateExecutionEnvironment(&argc, &argv,

                               jrepath, sizeof(jrepath),

                               jvmpath, sizeof(jvmpath),

                               original_argv);

加载JVM:

1   if (!LoadJavaVM(jvmpath, &ifn)) {
2       exit(6);
3     }

设置Classpath:

  SetClassPath

设置各种参数

1   /* set the -Dsun.java.command pseudo property */
2     SetJavaCommandLineProp(classname, jarfile, argc, argv);

4     /* Set the -Dsun.java.launcher pseudo property */
5     SetJavaLauncherProp();

7     /* set the -Dsun.java.launcher.* platform properties */
8     SetJavaLauncherPlatformProps();

设置线程栈大小:

1   if (threadStackSize == 0) {
2       struct JDK1_1InitArgs args1_1;
3       memset((void*)&args1_1, 0, sizeof(args1_1));
4       args1_1.version = JNI_VERSION_1_1;
5       ifn.GetDefaultJavaVMInitArgs(&args1_1);  /* ignore return value */
6       if (args1_1.javaStackSize > 0) {
7          threadStackSize = args1_1.javaStackSize;
8       }
9     }

创建一个新的线程创建JVM并调用main方法。

 1  { /* Create a new thread to create JVM and invoke main method */
 2       struct JavaMainArgs args;
 3 
 4       args.argc = argc;
 5       args.argv = argv;
 6       args.jarfile = jarfile;
 7       args.classname = classname;
 8       args.ifn = ifn;
 9 
10       return ContinueInNewThread(JavaMain, threadStackSize, (void*)&args);
11     }

最后在这个线程里调用了JavaMain,接下来是JavaMain的代码:

1 int JNICALL
2 JavaMain(void * _args)
3 {
4     struct JavaMainArgs *args = (struct JavaMainArgs *)_args;
5     int argc = args->argc;
6     char **argv = args->argv;
7     char *jarfile = args->jarfile;
8     char *classname = args->classname;

下面就是调用Java里的main方法了,当然首先是加载类:

1 if (jarfile != 0) {
2         mainClassName = GetMainClassName(env, jarfile);

根据class的名字加载class:

1  classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
2         if (classname == NULL) {
3             ReportExceptionDescription(env);
4             goto leave;
5         }
6         mainClass = LoadClass(env, classname);

还有其他情况:

 1   mainClassName = NewPlatformString(env, classname);
 2       if (mainClassName == NULL) {
 3         const char * format = "Failed to load Main Class: %s";
 4         message = (char *)JLI_MemAlloc((strlen(format) + strlen(classname)) *
 5                                    sizeof(char) );
 6         sprintf(message, format, classname);
 7         messageDest = JNI_TRUE;
 8         goto leave;
 9       }
10       classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
11       if (classname == NULL) {
12         ReportExceptionDescription(env);
13         goto leave;
14       }
15       mainClass = LoadClass(env, classname);

接下来就是调用java中的main方法了,这是一个静态方法:

1   /* Get the application's main method */
2     mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
3                                        "([Ljava/lang/String;)V");

确认main方法是public的

 1  {    /* Make sure the main method is public */
 2         jint mods;
 3         jmethodID mid;
 4         jobject obj = (*env)->ToReflectedMethod(env, mainClass,
 5                                                 mainID, JNI_TRUE);
 6 
 7         if( obj == NULL) { /* exception occurred */
 8             ReportExceptionDescription(env);
 9             goto leave;
10         }
11 
12         mid =
13           (*env)->GetMethodID(env,
14                               (*env)->GetObjectClass(env, obj),
15                               "getModifiers", "()I");

1  mods = (*env)->CallIntMethod(env, obj, mid);
2         if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods))  */
3             message = "Main method not public.";
4             messageDest = JNI_TRUE;
5             goto leave;
6         }

构建参数:

1    /* Build argument array */
2     mainArgs = NewPlatformStringArray(env, argv, argc);
3     if (mainArgs == NULL) {
4         ReportExceptionDescription(env);
5         goto leave;
6     }

调用main方法:

1  /* Invoke main method. */
2     (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);

脱离线程:

 1   /*
 2      * Detach the main thread so that it appears to have ended when
 3      * the application's main method exits.  This will invoke the
 4      * uncaught exception handler machinery if main threw an
 5      * exception.  An uncaught exception handler cannot change the
 6      * launcher's return code except by calling System.exit.
 7      */
 8     if ((*vm)->DetachCurrentThread(vm) != 0) {
 9         message = "Could not detach main thread.";
10         messageDest = JNI_TRUE;
11         ret = 1;
12         goto leave;
13     }

销毁虚拟机,得等所有线程都结束,因为虚拟机是一个守护线程:

 1 leave:
 2     /*
 3      * Wait for all non-daemon threads to end, then destroy the VM.
 4      * This will actually create a trivial new Java waiter thread
 5      * named "DestroyJavaVM", but this will be seen as a different
 6      * thread from the one that executed main, even though they are
 7      * the same C thread.  This allows mainThread.join() and
 8      * mainThread.isAlive() to work as expected.
 9      */
10     (*vm)->DestroyJavaVM(vm);

回过头来比较重要的几个方法:
1 LoadClass
2 GetStaticMethodID
3 GetMethodID
4 CallIntMethod
5 CallStaticVoidMethod
6 ContinueInNewThread
7 LoadJavaVM
这些方法基本上就是我们平常写代码比较关注的了。

LoadClass

 1 /*
 2  * Loads a class, convert the '.' to '/'.
 3  */
 4 static jclass
 5 LoadClass(JNIEnv *env, char *name)
 6 {
 7     char *buf = JLI_MemAlloc(strlen(name) + 1);
 8     char *s = buf, *t = name, c;
 9     jclass cls;
10     jlong start, end;
11 
12     if (_launcher_debug)
13         start = CounterGet();
14 
15     do {
16         c = *t++;
17         *s++ = (c == '.') ? '/' : c;
18     } while (c != '\0');
19     cls = (*env)->FindClass(env, buf);
20     JLI_MemFree(buf);
21 
22     if (_launcher_debug) {
23         end   = CounterGet();
24         printf("%ld micro seconds to load main class\n",
25                (long)(jint)Counter2Micros(end-start));
26         printf("----_JAVA_LAUNCHER_DEBUG----\n");
27     }
28 
29     return cls;
30 }

其中有个主要方法是调用JNIEnv的FindClass方法去找class:
在下载的源代码中jni.h位于share/vm/prims,在网上找到如下代码:
http://home.pacifier.com/~mmead/jni/cs510ajp/jni.h 
  1 /*

 2  * We use inlined functions for C++ so that programmers can write:
 3  * 
 4  *    env->FindClass("java/lang/String")
 5  *
 6  * in C++ rather than:
 7  *
 8  *    (*env)->FindClass(env, "java/lang/String")
 9  *
10  * in C.
11  */
12 
13 struct JNIEnv_ {
14     const struct JNINativeInterface_ *functions;
15     void *reserved0;
16     void *reserved1[6];
17 #ifdef __cplusplus
18 
19     jint GetVersion() {
20         return functions->GetVersion(this);
21     }
22     jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
23                jsize len) {
24         return functions->DefineClass(this, name, loader, buf, len);
25     }
26     jclass FindClass(const char *name) {
27         return functions->FindClass(this, name);
28     }

JNI是依赖于Java Runtime Interface的:

/* 

 * We used part of Netscape's Java Runtime Interface (JRI) as the starting

 * point of our design and implementation.

 */

具体可以看这里这里
注意JNINativeInterface_ 这个结构体,实际上之前说的那些方法,最终都是调用了它:

  jclass (JNICALL *FindClass)  (JNIEnv *env, const char *name);
  jmethodID (JNICALL *GetStaticMethodID)       (JNIEnv *env, jclass clazz, const char *name, const char *sig);

 void (JNICALL *CallStaticVoidMethod)

      (JNIEnv *env, jclass cls, jmethodID methodID, );

这种写法在C++里叫inline function,调用方法就是像上面写的那样:

 1 /*
 2  * We use inlined functions for C++ so that programmers can write:
 3  * 
 4  *    env->FindClass("java/lang/String")
 5  *
 6  * in C++ rather than:
 7  *
 8  *    (*env)->FindClass(env, "java/lang/String")
 9  *
10  * in C.
11  */

share/vm/prims中包含了jni的大量实现代码,对于上面的概述进行了细化,如果想知道怎么加载例如FindClass这样的方法,以及它们内部到底干了什么,就得看这部分了。

 

LoadJavaVM
LoadJavaVM和DestroyJavaVM等跟VM有关的方法,都与share/vm/prims中的jvm.h,jvm.cpp有关。
这个方法解释如下:

 1 /*
 2  * This file contains additional functions exported from the VM.
 3  * These functions are complementary to the standard JNI support.
 4  * There are three parts to this file:
 5  *
 6  * First, this file contains the VM-related functions needed by native
 7  * libraries in the standard Java API. For example, the java.lang.Object
 8  * class needs VM-level functions that wait for and notify monitors.
 9  *
10  * Second, this file contains the functions and constant definitions
11  * needed by the byte code verifier and class file format checker.
12  * These functions allow the verifier and format checker to be written
13  * in a VM-independent way.
14  *
15  * Third, this file contains various I/O and nerwork operations needed
16  * by the standard Java I/O and network APIs.
17  */
18 
19 /*
20  * Bump the version number when either of the following happens:
21  *
22  * 1. There is a change in JVM_* functions.
23  *
24  * 2. There is a change in the contract between VM and Java classes.
25  *    For example, if the VM relies on a new private field in Thread
26  *    class.
27  */

这里面包括了VM这个层次上的抽象,大到创建Java线程,加载类,解析字节码,小到JVM原生支持的Java方法。
下面是从bootstrap class loader的代码,Java的根class loader是c++实现的。

 1 // Returns a class loaded by the bootstrap class loader; or null
 2 // if not found.  ClassNotFoundException is not thrown.
 3 //
 4 // Rationale behind JVM_FindClassFromBootLoader
 5 // a> JVM_FindClassFromClassLoader was never exported in the export tables.
 6 // b> because of (a) java.dll has a direct dependecy on the  unexported
 7 //    private symbol "_JVM_FindClassFromClassLoader@20".
 8 // c> the launcher cannot use the private symbol as it dynamically opens
 9 //    the entry point, so if something changes, the launcher will fail
10 //    unexpectedly at runtime, it is safest for the launcher to dlopen a
11 //    stable exported interface.
12 // d> re-exporting JVM_FindClassFromClassLoader as public, will cause its
13 //    signature to change from _JVM_FindClassFromClassLoader@20 to
14 //    JVM_FindClassFromClassLoader and will not be backward compatible
15 //    with older JDKs.
16 // Thus a public/stable exported entry point is the right solution,
17 // public here means public in linker semantics, and is exported only
18 // to the JDK, and is not intended to be a public API.
19 
20 JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
21                                               const char* name))
22   JVMWrapper2("JVM_FindClassFromBootLoader %s", name);

为了对这个重要的文件有个整体认识,下面再摘录几段:

/*************************************************************************

 PART 1: Functions for Native Libraries

 ************************************************************************/
/*

 * java.lang.Object

 */

JNIEXPORT jint JNICALL

JVM_IHashCode(JNIEnv *env, jobject obj);

JNIEXPORT void JNICALL

JVM_MonitorWait(JNIEnv *env, jobject obj, jlong ms);

JNIEXPORT void JNICALL

JVM_MonitorNotify(JNIEnv *env, jobject obj);

JNIEXPORT void JNICALL

JVM_MonitorNotifyAll(JNIEnv *env, jobject obj);

JNIEXPORT jobject JNICALL

JVM_Clone(JNIEnv *env, jobject obj);

可以看到对象上有个监视器来执行线程的Wait和Notify。
PartI中跟线程有关的:

 1 /*
 2  * java.lang.Thread
 3  */
 4 JNIEXPORT void JNICALL
 5 JVM_StartThread(JNIEnv *env, jobject thread);
 6 
 7 JNIEXPORT void JNICALL
 8 JVM_StopThread(JNIEnv *env, jobject thread, jobject exception);
 9 
10 JNIEXPORT jboolean JNICALL
11 JVM_IsThreadAlive(JNIEnv *env, jobject thread);
12 
13 JNIEXPORT void JNICALL
14 JVM_SuspendThread(JNIEnv *env, jobject thread);
15 
16 JNIEXPORT void JNICALL
17 JVM_ResumeThread(JNIEnv *env, jobject thread);
18 
19 JNIEXPORT void JNICALL
20 JVM_SetThreadPriority(JNIEnv *env, jobject thread, jint prio);
21 
22 JNIEXPORT void JNICALL
23 JVM_Yield(JNIEnv *env, jclass threadClass);
24 
25 JNIEXPORT void JNICALL
26 JVM_Sleep(JNIEnv *env, jclass threadClass, jlong millis);
27 
28 JNIEXPORT jobject JNICALL
29 JVM_CurrentThread(JNIEnv *env, jclass threadClass);
30 
31 JNIEXPORT jint JNICALL
32 JVM_CountStackFrames(JNIEnv *env, jobject thread);
33 
34 JNIEXPORT void JNICALL
35 JVM_Interrupt(JNIEnv *env, jobject thread);
36 
37 JNIEXPORT jboolean JNICALL
38 JVM_IsInterrupted(JNIEnv *env, jobject thread, jboolean clearInterrupted);
39 
40 JNIEXPORT jboolean JNICALL
41 JVM_HoldsLock(JNIEnv *env, jclass threadClass, jobject obj);
42 
43 JNIEXPORT void JNICALL
44 JVM_DumpAllStacks(JNIEnv *env, jclass unused);
45 
46 JNIEXPORT jobjectArray JNICALL
47 JVM_GetAllThreads(JNIEnv *env, jclass dummy);

第二部分主要是验证class格式

 1 /*************************************************************************
 2  PART 2: Support for the Verifier and Class File Format Checker
 3  ************************************************************************/
 4 /*
 5  * Return the class name in UTF format. The result is valid
 6  * until JVM_ReleaseUTf is called.
 7  *
 8  * The caller must treat the string as a constant and not modify it
 9  * in any way.
10  */
11 JNIEXPORT const char * JNICALL
12 JVM_GetClassNameUTF(JNIEnv *env, jclass cb);
13 
14 /*
15  * Returns the constant pool types in the buffer provided by "types."
16  */
17 JNIEXPORT void JNICALL
18 JVM_GetClassCPTypes(JNIEnv *env, jclass cb, unsigned char *types);
19 
20 /*
21  * Returns the number of Constant Pool entries.
22  */
23 JNIEXPORT jint JNICALL
24 JVM_GetClassCPEntriesCount(JNIEnv *env, jclass cb);
25 
26 /*
27  * Returns the number of *declared* fields or methods.
28  */
29 JNIEXPORT jint JNICALL
30 JVM_GetClassFieldsCount(JNIEnv *env, jclass cb);

第三部分主要是对IO和网络的支持,包括了File和Socket

 1 /*************************************************************************
 2  PART 3: I/O and Network Support
 3  ************************************************************************/
 4 
 5 /* Note that the JVM IO functions are expected to return JVM_IO_ERR
 6  * when there is any kind of error. The caller can then use the
 7  * platform specific support (e.g., errno) to get the detailed
 8  * error info.  The JVM_GetLastErrorString procedure may also be used
 9  * to obtain a descriptive error string.
10  */
11 #define JVM_IO_ERR  (-1)
12 
13 /* For interruptible IO. Returning JVM_IO_INTR indicates that an IO
14  * operation has been disrupted by Thread.interrupt. There are a
15  * number of technical difficulties related to interruptible IO that
16  * need to be solved. For example, most existing programs do not handle
17  * InterruptedIOExceptions specially, they simply treat those as any
18  * IOExceptions, which typically indicate fatal errors.
19  *
20  * There are also two modes of operation for interruptible IO. In the
21  * resumption mode, an interrupted IO operation is guaranteed not to
22  * have any side-effects, and can be restarted. In the termination mode,
23  * an interrupted IO operation corrupts the underlying IO stream, so
24  * that the only reasonable operation on an interrupted stream is to
25  * close that stream. The resumption mode seems to be impossible to
26  * implement on Win32 and Solaris. Implementing the termination mode is
27  * easier, but it's not clear that's the right semantics.
28  *
29  * Interruptible IO is not supported on Win32.It can be enabled/disabled
30  * using a compile-time flag on Solaris. Third-party JVM ports do not
31  * need to implement interruptible IO.
32  */
33 #define JVM_IO_INTR (-2)

有个结论是:整个Hotspot JVM是以JRI为起点进行设计的基于JNI的框架,可以算是一个插件架构,通过JNI的机制扩展JVM自身。
另外在share/vm/prims中还能看到大量的jvmti开头的文件,那么jvmti是什么东东呢?那么看这里
  The JVMTM Tool Interface (JVM TI) is a
new native programming interface for use by tools. It provides both a way to inspect the state and to control the execution of applications running in the Java virtual machine (JVM). JVM TI supports the full breadth of tools that need access to JVM state,
including but not limited to: profiling, debugging, monitoring, thread analysis, and coverage analysis tools.

Note: JVM TI replaces the Java Virtual Machine Profiler Interface (JVMPI) and the Java Virtual Machine Debug Interface (JVMDI). JVMPI and JVMDI will be removed in the next major release of J2SETM.

先写到这吧,下面一篇应该会从整个的JVM架构来讲一下,然后再去对照Inside JVM和JVM规范去理解重点代码,看其是如何实现的。我关注的重点是OO,IO,多线程以及GC,JIT这几个方面的实现。另外一部分重点是更基础的,JVM真正在执行层面上如何运行Java字节码的,那些堆,栈,栈帧,PC如何创建,如何执行指令。
给自己做一个规划吧,列个提纲:
1 JVM总体架构
2 JVM内存模型以及运行时
3 多线程
4 GC
5 OO
6 IO
7 JIT

JVM源码分析-Java运行的更多相关文章

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

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

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

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

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

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

  4. JVM源码分析之Metaspace解密

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. Kubernetes-4:kubectl常用命令总结

    kubectl常用命令 1.kubectl get 显示资源 ## 查看pod列表,若pod后不指定名称空间(namespace),则默认为default名称空间 kubectl get pod ## ...

  2. Redis实战9-全局唯一ID

    发布优惠券的时候,每个店铺都可以发布优惠券,当用户抢购的时候,优惠券表中的id如果使用数据库的自增长ID会存在以下问题: 1:id的规律太明显,容易被刷 2:当数据量很大的时候,会受到单表数据的限制 ...

  3. vue 根据div id 滚动到指定view到可视视图中

    <div id="name" @click="scrollToViewById('name')"> ...... </div> scro ...

  4. 在.net core使用Serilog,只要简单的三步

    第一步:在项目上用nuget安装 Serilog.AspNetCore 最新的稳定版即可 ,安装这个会把其他需要的包都给包含着 第二步:修改 Program.cs 的 CreateHostBuilde ...

  5. 以太坊Rollup方案之 arbitrum(1)

    什么是Rollup? 以太坊的Rollup扩容是一种Layer 2(第二层)扩容解决方案,旨在提高以太坊区块链的交易吞吐量和性能.它通过将大量的交易数据转移到以太坊区块链之外的第二层网络来实现这一目标 ...

  6. DVI与VGA有什么区别?

    DVI接口:DVI(Digital Visual Interface),即数字视频接口.DVI接口速度快.画面清晰.支持HDCP协议. VGA接口:VGA(Video Graphics Array)即 ...

  7. TypeScript 高级教程 – 把 TypeScript 当强类型语言使用 (第一篇)

    前言 原本是想照着 TypeScript 官网 handbook 写个教程的. 但提不起那个劲... 所以呢, 还是用我自己的方式写个复习和进阶笔记就好了呗. 以前写过的 TypeScript 笔记: ...

  8. CSS – display, visibility, opacity, transparent 的区别

    前言 要让一个元素"消失", 有 3 种做法. 它们有一点点的不同. 在实战时要清楚什么时候用什么哦. 例子说明 <div class="abc"> ...

  9. Web核心

    JavaWeb 技术栈   

  10. USB眼图常识

    最近一段时间,项目需求,需要对USB眼图进行摸底测试.测试过程很简单,然而debug之路却很不简单.不简单往往也就意味着带来了不少收获. 示波器跨接在接收滤波器的输出端,调整示波器扫描周期,使示波器水 ...