jvm15版本源码阅读之main方法调用
关于初始化的部分已经分析完了,这里回到最初的方法调用中.
之前的启动并初始化vm是在Threads::create_vm方法中实现的,该方法在JNI_CreateJavaVM_inner中,回到该方法.
1 返回之前的方法
1.1 返回JNI_CreateJavaVM_inner方法
回到JNI_CreateJavaVM_inner方法中,

此时就发出了启动main线程的事件通知,接下来就是启动main方法.
1.2 返回到JavaMain方法
JNI_CreateJavaVM_inner的上一层方法调是JavaMain,在该方法中首先启动并初始化了vm,接着就是要执行main方法了.

2 执行main方法
这里执行了env的CallStaticVoidMethod方法,和JNI_CreateJavaVM_inner一样,也是jni的方法,进入该方法

调用了jni_invoke_static方法,进入该方法

1 获取输入参数个数
2 判断是否是静态方法
3 设置方法参数值
4 设置返回类型
5 执行call方法
进入最后的call方法

进入call_helper方法

StubRoutines::call_stub()是方法入口,是调用main方法的入口,进入call_stub方法

此方法是函数指针,直接输入参数进入执行方法的地址_call_stub_entry,该地址在初始化时已经完成,因为是StubRoutines,所以此时肯定已完成.
这里需要关注的是call_stub方法的entrypoint,也就是java的main方法的进入点

2.1 entry_point
entry_point是一个例程,关于例程也是vm的一个概念,之后会总结vm中使用的一些概念.此处的例程可以认为是引导程序,或者也可以看作是java中的utils工具类.
关于该例程的生成是在初始化时生成的,也就是在TemplateInterpreter::initialize方法中,之前分析过.之后会调用TemplateInterpreterGenerator::generate_normal_entry这个方法,进入该方法,由于该方法比较长,这里分别分析
1 第一部分

该处的entry_point已经确认,就是main的入口处
2 第二部分

设置栈溢出的检查部分.
3 第三部分

创建调用栈的固定部分,进入该方法

可以看到,都是使用的汇编来创造的栈,以__ push(rax)为例,进入

可见,vm使用了c/c++来模拟了汇编指令,这么做的目的也是配合模拟方法栈的调用过程.
4 第四部分

__ dispatch_next就是下一条指令,也是main方法编译后,java字节码的第二条指令,自此开始正式执行java字节码,在物理cpu上执行对应的机器码.
3 执行main完毕
回到JavaMain方法,

之前分析了(*env)->CallStaticVoidMethod方法,第二步就是检查是否抛出异常,第三部就是LEAVE方法,此方法是一个宏,进入

正常结束的话会执行DestroyJavaVM方法,进入

进入jni_DestroyJavaVM_inner方法

进入Threads::destroy_vm方法

可见,main方法执行完,jvm的所有线程都退出,也就算该程序彻底退出了.
4 总结
每一个java程序的运行都是基于jvm的,并且运行完就彻底退出了.
一句hello word代码,从main方法进入,到退出,是在虚拟机的层面运行的,虽然代码简单,但是jvm从初始化到方法执行到退出执行了非常多的操作,总结起来:
1 main方法进入之前调用例程创建方法栈
2 基于栈执行字节码
3 执行完毕退出main方法
4 退出jvm
Jvm是极其精致的,虽然和其它一些大型的虚拟机相比有些“小”,但是绝对不逊于那些大型的虚拟机.更何况有些虚拟机直接使用了物理cpu作为独用cpu,但是jvm没有,所以jvm是高效轻量级的虚拟机.
Jvm本质是cpp程序,整体上是cpp工程,但是具有极高的技术研究价值,精致且巧妙的布局和混入的c和汇编,使得看上去浑然一体.在运行上并没有独占物理cpu,仅从cpu的角度模拟了程序的执行,堆栈哪个都不缺,而且还有所增强,比如外挂会抛出的异常以及修改字节码.
能把编写jvm的cpp书写的如此柔美,这些作者的智慧和对此的理解是那么的从容,使得阅读这些cpp变得很惬意,不禁感叹让很多人使用jvm显得多么慷慨.这确实是一件很美妙的事.

583

被折叠的 条评论
为什么被折叠?



