我们写的主类中的main()
方法是如何被Java
虚拟机调用到的?在Java
类中的一些方法会被由C/C++
编写的HotSpot
虚拟机的C/C++函数调用,不过由于Java方法与C/C++函数的调用约定不同,所以并不能直接调用,需要JavaCalls::call()
这个函数辅助调用。(我把由C/C++编写的叫函数,把Java编写的叫方法,后续也会延用这样的叫法)如下图所示。
从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类中,
这个类的定义如下:
从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类中,
这个类的定义如下:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
如上的函数都是自解释的,通过名称我们就能看出这些函数的作用。其中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()函数的线程执行的主要逻辑如下:
?
1 2 3 |
|
在如上线程中会启动另外一个线程执行JavaMain()函数,如下:
?
1 2 3 4 5 6 |
|
以上步骤都还在java launcher
的控制下;当控制权转移到JavaMainClass.main()
方法之后就没java launcher
什么事了,等JavaMainClass.main()
方法返回之后java launcher
才接手过来清理和关闭JVM。
下面看一下调用Java主类main()
方法时会经过的主要方法及执行的主要逻辑,如下:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
后面3个步骤是在编译执行的模式下,不过后续我们从解释执行开始研究,所以需要为虚拟机配置-Xint选项,有了这个选项后,Java主类的main()
方法就会解释执行了。
在调用Java主类main()
方法的过程中,我们看到了虚拟机是通过JavaCalls::call()
函数来间接调用main()
方法的,下一篇我们研究一下具体的调用逻辑。
到此这篇关于关于Java虚拟机HotSpot的文章就介绍到这了,更多相关Java虚拟机HotSpot内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!
原文链接:https://www.heapdump.cn/article/2860175
本文链接:https://my.lmcjl.com/post/3122.html
4 评论