HotSpot VM Runtime负责启动和停止hotSpot VM。这篇文章简单介绍了一下一个Web应用的启动和停止。
启动HotSpot VM的组件被称作启动器(launcher)。有许多HotSpot VM启动器,最常用的有Unix/Linux上的java 命令和Windows的java和javaw命令。
启动器执行一系列的操作来启动HotSpot Vm,这些步骤总结如下:
1、解析命令行选项
一些命令行选项被启动器直接使用,比如:-client或者-server,这些命令决定加载JIT complier,其他的命令行被传送到已经启动的HotSpot VM。
2、如果关于堆或者JIT complier的选项没有被命令行明确的置顶,则建立Java堆的大小和JIT complier的类型(client or server)。
这些默认值以来底层系统配置和操作系统而改变。
3、建立环境变量,比如LD_LIBRARY_PATH和CLASSPATH
4、如果命令行里没有指定主入口类(Main -CLASS),那启动器就会从JAR包的清单中得到主入口类的名字。
5、在一个新创建的非原生线程中利用标准的Java本地接口方法JNI_CreateJavaVM建立HotSpot VM。
与一个非原生的线程形成对比,一个源生的线程是当一个新的进程建立的时候被操作系统内核分配的第一个线程。因此,当一个HotSpot VM被创建,源生线程是运行在新建立的HotSpot进程中的第一个被操作系统内核分配的线程。在一个非源生的线程中创建HotSpot VM提供了定制HotSpot VM的功能,例如在Windows上改变栈的大小。
6、一旦HotSpot VM被创建和初始化了,Java的主类(Main-Class)就被加载,启动器就从Main-Class得到了main方法的属性。
7、HotSpot VM利用 Java Native Interface方法 CallStaticVoidMethod调用Java的main方法。同时传入来自命令行的参数序列。
此时,HotSpot VM就在执行由命令行指定的Java程序。
一旦一段Java程序,或者Java main方法执行完成,HotSpot VM必须检查和清理在程序或者方法的执行过程中可能发生的异常。此外,方法的退出状态和程序的退出状态必须被传回他们的调用者。然后Java main方法使用Java Native Interface方法DetachCurrentThread从HotSpot VM中脱离。当HotSpot VM调用了DetachCurrentThread,线程的数量将减少,然后Java Native Interface便知道了什么时候去安全的停止HotSpot Vm并且确保一个线程不再在HotSpot VM中执行并伴随着在栈中没有活跃的帧。
JNI_CreateJavaVM详解
HotSpot Vm的JNI_CreateJavaVM方法的实现在他在HotSpot VM的启动中调用的时候表现在下面的操作。
1、确保在同一时间没有两个线程调用这个方法,并且只有一个HotSpot VM实例在进程中被创建。
因为HotSpot VM创建不能被重新初始化的静态数据结构,在一个进程空间,一个初始化的点到达的时候只能有一个HotSpot VM被创建。对于开发HotSpot VM的工程师来说,启动HotSpot VM的这个阶段是一个类似于只能进不能退的阶段。
2、检查确保支持Java Native Interface的版本。并且为垃圾收集日志初始化好输出流。
3、操作系统模块被初始化,比如随机数生成器,当前进程id,高分辨率计数器、内存页大小和守护页。守护页是非随机存取的内存页,用来限制内存区域的访问。例如:操作系统经常把一个守护页线程栈的栈顶,确保引用不到栈底区域被困住的区域。
4、传递给JNI_CreateJavaVM的命令行参数和属性被解析和存储以备后来使用。
5、标准的系统属性被被初始化,比如:java.version,java.vendor,os.name等等。
6、支持同步、栈、存储器、安全点页的模块被初始化。
7、组件库比如:libzip、libhpi、libjava和libthread被装载。
8、信号处理程序被初始化和设置。
9、线程库被初始化。
10、输出流记录器被初始化。
11、将被用到的代理库(hprof,jdi)完成初始化和启动。
12、持有线程操作需要的特殊数据的线程状态和本地线程存储被初始化。
13、HotSpot VM全局数据的一部分被初始化,比如:事件log,操作系统同步原语,性能统计存储器、内存分配器。
14、这时HotSpot VM就能够创建线程。主线程的Java版本被创建并且隶属于当前的操作系统线程。然而这个线程不被添加到被知道的线程列表中。
15、Java层级的同步被初始化和激活。
16、启动类加载器、代码缓存、解释器、JIT编译器、Java Native Interface、系统字典和整个程序被初始化。
17、现在Java主线程添加到被知道的线程列表中,整个程序即一组需要的全局数据结构将进行完整性检查。
执行所有的HotSpot Vm的重要功能的HotSpot VM 线程被创建,这时,合适的JVMTI(JVM工具接口)将被发布来通知HotSpot VM当前的状态。
18、下列的Java类java.lang.String, java.lang.System, java.lang.Thread, java.lang.ThreadGroup, java.lang.reflect.Method,java.lang.ref.Finalizer, java.lang.Class和剩下的Java类被加载和初始化。这时HotSpot VM就已经被初始化并处于运行状态,但是还没有完全起作用。
19、HotSpot VM信号处理线程开始工作,JIT编译器被初始化,HotSpot的编译代理线程开始工作。其他的HotSpot VM辅助线程比如监控线程、采样线程开始工作。这时HotSpot VM完全起作用了。
20、最后JNIEnv被绑定并返回给调用程序,然后HotSpot VM准备给新的JNI请求服务。
DestroyJavaVM 详解
当HotSpot VM启动的过程中出现错误的时候,DestroyJavaVM方法将被从HotSpot启动器中被调用来关闭HotSpot VM。在HotSpot启动后,当有一个非常严重的错误发生时DestroyJavaVM方法也可以在执行期间被HotSpot VM调用。
HotSpot VM通过DestroyJavaVM方法来关闭需要经历一下一些步骤:
1、等到只有一个普通线程执行的时候。注意:HotSpot VM仍然在起作用。
2、调用Java方法java.lang.Shutdown.shutdown(),该方法调用了Java层级的关闭钩子,如果在退出时终结为true(if finalization-on-exit is true),然后运行Java对象的终结器.
3、通过运行SpotHot层级的关闭钩子(那些通过JVM_OnExit()注册的),停止以下HotSpot线程:分析器、采集器、监控器和垃圾回收线程来为HotSpot VM的退出做准备。发送状态事件给JVMTI,停用JVMTI,然后停止信号线程。
4、调用HotSpot方法JavaThread::exit()释放Java Native Interface句柄块,移除守护页,从被知道的线程列表中当前线程,从这时起,HotSpotVM不能执行任何Java代码。
5、停止HotSpot VM线程。这将引起HotSpot VM将剩下的HotSpot VM线程带到一个安全点然后停止JIT编译器线程。
6、停止追踪Java NativeInterface,HotSpot VM,和JVMTI屏障。
7、将可能运行在本地代码的线程设置为"VM exited"标志。
8、删除当前线程。
9、删除或者移除所有输入/输出流,释放性能统计存储器资源。
10、最后返回调用程序。