天天看点

第1篇-关于Java虚拟机HotSpot,开篇说的简单点

开讲java运行时,这一篇讲一些简单的内容。我们写的主类中的main()方法是如何被java虚拟机调用到的?在java类中的一些方法会被由c/c++编写的hotspot虚拟机的c/c++函数调用,不过由于java方法与c/c++函数的调用约定不同,所以并不能直接调用,需要javacalls::call()这个函数辅助调用。(我把由c/c++编写的叫函数,把java编写的叫方法,后续也会延用这样的叫法)如下图所示。

第1篇-关于Java虚拟机HotSpot,开篇说的简单点

从c/c++函数中调用的一些java方法主要有:

(1)java主类中的main()方法;

(2)java主类装载时,调用javacalls::call()函数执行checkandloadmain()方法;

(3)类的初始化过程中,调用javacalls::call()函数执行的java类初始化方法<clinit>,可以查看javacalls::call_default_constructor()函数,有对<clinit>方法的调用逻辑;

(4)我们先省略main方法的执行流程(其实main方法的执行也是先启动一个javamain线程,套路都是一样的),单看某个javathread的启动过程。javathread的启动最终都要通过一个native方法java.lang.thread#start0()方法完成的,这个方法经过解释器的native_entry入口,调用到了jvm_startthread()函数。其中的static void thread_entry(javathread* thread, traps)函数中会调用javacalls::call_virtual()函数。javathread最终会通过javacalls::call_virtual()函数来调用字节码中的run()方法;

(5)在systemdictionary::load_instance_class()这个能体现双亲委派的函数中,如果类加载器对象不为空,则会调用这个类加载器的loadclass()函数(通过call_virtual()函数来调用)来加载类。

当然还会有其它方法,这里就不一一列举了。通过javacalls::call()、javacalls::call_helper()等函数调用java方法,这些函数定义在javacalls类中,这个类的定义如下:

如上的函数都是自解释的,通过名称我们就能看出这些函数的作用。其中javacalls::call()函数是更低一层的通用接口。java虚拟机规范定义的字节码指令共有5个,分别为invokestatic、invokedynamic、invokestatic、invokespecial、invokevirtual几种方法调用指令。这些call_static()、call_virtual()函数内部调用了call()函数。这一节我们先不介绍各个方法的具体实现。下一篇将详细介绍。  

我们选一个重要的main()方法来查看具体的调用逻辑。如下基本照搬r大的内容,不过我略做了一些修改,如下:

假设我们的java主类的类名为javamainclass,下面为了区分java launcher里c/c++的main()与java层程序里的main(),把后者写作javamainclass.main()方法。

从刚进入c/c++的main()函数开始:

启动并调用hotspot虚拟机的main()函数的线程执行的主要逻辑如下:

在如上线程中会启动另外一个线程执行javamain()函数,如下:

以上步骤都还在java launcher的控制下;当控制权转移到javamainclass.main()方法之后就没java launcher什么事了,等javamainclass.main()方法返回之后java launcher才接手过来清理和关闭jvm。

下面看一下调用java主类main()方法时会经过的主要方法及执行的主要逻辑,如下:

后面3个步骤是在编译执行的模式下,不过后续我们从解释执行开始研究,所以需要为虚拟机配置-xint选项,有了这个选项后,java主类的main()方法就会解释执行了。

在调用java主类main()方法的过程中,我们看到了虚拟机是通过javacalls::call()函数来间接调用main()方法的,下一篇我们研究一下具体的调用逻辑。