CVM虚拟机的启动过程

时间:2022-06-11 20:36:56

位于Java_md.c中的main() 函数调用与ANSI兼容的ansiJavaMain() 函数来实现自举。ansiJavaMain()做的第一件事情是转换和处理虚拟机的命令行参数,然后调用定义在JNI Invocation API中定义的JNI_CreateJavaVM() 函数来创建和初始化CVM。在这个工程当中,它初始化所有的VM全局状态变量(VM global states,位于global.c中的全局变量CVMGlobalState CVMglobals当中),包括system mutexes, Java heap, threads, classloaders 等等。在虚拟机成功创建之后,它载入Java应用的main class并调用main Java方法。最先调用sun.misc.CVM类中的runMain()方法,该方法根据Java应用的主类名反射生成class,然后在调用该class中定义的main()方法。

所有的初始化工作都在JNI_CreateJavaVM(JavaVM **p_jvm, void **p_env, void *args)函数中进行,该函数调用CVMinitVMGlobalState(),initializeSystemClasses()等众多初始化函数。

 

1          Preloader Initialization

CVMpreloaderInit()

CVM支持class ROMization(在编译的时候), 有时我们也称它为class preloadingVM一开始需要做的少数几件事之一就是初始化ROMized classes。对于每一个ROMized class blocks (CVMClassBlock 是存储Java类元数据的主要数据结构),虚拟机找到它们(class blocks)相应的Java实例并通过填充它们的class block pointer, classloader point等对它们进行初始化。虚拟机也对被压缩的字符串数据进行迭代并通过填充字符串valueoffset和长度等来构造匹配的Java实例。注意: 这些ROMized classes并不存活在Java heap当中。

CVM中,method的数据结构(CVMMethdBlock)由不可变和可变部分组成。不可变部分包括方面和type ID,方法表索引(methodtable index)等。一旦被JCC(ROMizer)classloader初始化后,这些域将绝不会被修改。可变部分包括调用者(invoker pointer)指针,编译后代码的起始PC(compiled code start PC)等。作为preloader初始化的一部分,VM通过拷贝不变部分和填充可变部分来为每个ROMized的类初始化method data

 

 

2          System Mutexes Initialization

可能在CVMinitVMGlobalState()函数进行初始化

CVM有用遍及整个VM子系统的控制铜鼓的全局mutexes。这包括jit lock, heap lock, thread-list lock, class table lock, loader cache lock, global roots lock, weak global roots lock, typeid lock等。在自举工程当中,CVM创建并初始化这些全局locks

 

 

3          初始化GC Global Root Stacks

调用CVMinitGCRootStack()函数对各个stack进行初始化。初始化GC root stacks包括global roots stack (for allocating JNI global references and CVM global roots), weak global roots stack (for allocating JNI weak global references), class global roots stack, classloader global roots stack, protection domain global roots stack, and class table root stack(for all dynamically loaded classes). The global root stacks are used by GC for scanning live objects.

 

 

4          Typeid System Initialization

CVMinitVMGlobalState() -> CVMtypeidInit()

VM做的下一件事情就是初始化typeid system并注册一些常用typeids,例如: CVMglobals中的<init>, <clinit>, finalize 等。这可以避免在运行时重复的搜索这些typeid

 

5          Classes System Initialization

CVMinitVMGlobalState() -> CVMclassModuleInit()

CVMglobals.loaderCache = (CVMLoaderCacheEntry **)

       calloc(CVM_LOADER_CACHE_TABLE_SIZE, sizeof(CVMLoaderCacheEntry*));

the class loader cache 初始化,载入器缓冲区用来缓存所有系统所载入的<Class, ClassLoader>对。载入器保存了一个"(classID, loader) => cb"的映射。Cache中的所有类已经检验过满足class loader constraintsCache加快了类载入。

注意: "(classID, loader) => cb"影射中,"loader"initiating loader, 没有必要是最终定义(define)classloader。一个类载入器可以将类载入委派到另一个类载入器。

例如: "java/lang/Stringan applet class loader载入时,实际上产生的classNULL作为它的类载入器。然而,一个载入的类也将总get an entry with its actual class loader.

当一个新的类将被添加到cache当中的时候,必须确保它不会破坏任何已经存在的约束(constraints)

在执行过程当中,作为class连接的结果,可能会增加新的约束。在增加每一个新的约束前,必须确保cache中缓存的classes将满足新的约束。

 

 

6          初始化Java(Heap)

CVM使用-Xms, -Xmn –Xmx 选项来制定起始、最小和最大的堆尺寸。它从VM参数中提取这些尺寸信息,并分配一个起始大小的堆。在运行的时候,heap能够增长到最大尺寸。

CVMinitVMGlobalState -> CVMgcInitHeap(CVMOptions *options) -> CVMgcimplInitHeap -> CVMmemMap -> mmap

 

 

7          Preallocating Object Monitors and Exception Objects

初始化Monitor: CVMBool CVMeeSyncInitGlobal(CVMExecEnv *ee, CVMGlobalState *gs)

初始化异常对象: CVMinitVMGlobalState -> CVMID_allocNewInstance(ee, CVMsystemClass(java_lang_OutOfMemoryError),

 

VM创建一定初始数目的object monitors。这是虚拟机所知的、它所需要的最小数目。desperation exception objects包括OutOfMemoryError, *Error等。

 

 

8          JVMTI(tool interface) 初始化

/* jvmti global variables initialization */

    CVMjvmtiInitializeGlobals(&gs->jvmti);

如果支持debugger profiler, 那么CVM也执行JVMTI相关的初始化,TIdebugger profiler的替代品。

 

 

9          JIT初始化

  if (!CVMjitInit(ee, &gs->jit, options->jitAttributesStr))

/* Initialize the invoker cost of all ROMized methods */

    CVMpreloaderInitInvokeCost();

如果支持运行时编译(JIT),那么虚拟机需要执行JIT相关的初始化,包括初始化编译策略,后端编译器,代码缓存区等。

 

 

10     初始化一些系统类: initializeSystemClasses

JNI_CreateJavaVM  ->  static char* initializeSystemClasses

CVM_LVM 默认为false

ROMized的系统类需要明确地被VM进行初始化。首先,它首先通过执行它们的静态初始化块(static initializers) 来初始化那些不需要线程支持的系统类。这包括java.lang.Class, java.lang.String, java.lang.Shutdown, java.lang.ClassLoader等等。

然后它创建System ThreadGroup, main ThreadGroup, and main Thread objects.在线程对象初始化后,VM调用System.initializeSystemClass()来设置system properties, stdin, stdout, and stderr。在此期间,它也创建:

A Reference handler thread, which enqueues pending References

A Finalizer thread that runs the finalizer.

 

 

// 默认一般不会启动 LVM

static CVMBool CVMLVMinitSystemClasses(JNIEnv* env, jobject initThread)

{

systemClass = CVMcbJavaInstance(CVMsystemClass(java_lang_System));

       initMethodID = (*env)->GetStaticMethodID(env, systemClass,

                                           "initializeSystemClass",

                                           "()V");

       CVMassert(initMethodID != NULL);

       (*env)->CallStaticVoidMethod(env, systemClass, initMethodID);

 

 

11     解析剩余的命令行参数

/* Parse the command line options that are passed in (mostly done in sun.misc.CVM) */

    errorStr = CVMparseCommandLineOptions(env, initArgs,

                                     numUnrecognizedOptions);

虚拟机调用sun.misc.CVM.parseCommandLineOptions()来解析剩余的参数,在此过程当中,它增加用户定义的属性,并寻找main函数所在的类名。

现在CVM准备执行main方法, 它首先需要找到main Java class。它创建system classloader(sun.misc.Launcher$AppClassLoader),并使用这个classloader来搜索和载入main class。在这个class被成功载入后,它调用main()放吧并开始执行Java字节码。