返回顶部
首页 > 资讯 > 后端开发 > Python >关于Java虚拟机HotSpot
  • 953
分享到

关于Java虚拟机HotSpot

2024-04-02 19:04:59 953人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

我们写的主类中的main()方法是如何被Java虚拟机调用到的?在Java类中的一些方法会被由C/C++编写的HotSpot虚拟机的C/c++函数调用,不过由于Java方法与C/C+

我们写的主类中的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类中,

这个类的定义如下:


源代码位置:openjdk/hotspot/src/share/vm/runtime/javaCalls.hpp
 
class JavaCalls: AllStatic {
  static void call_helper(JavaValue* result, methodHandle* method, JavaCallArguments* args, TRAPS);
 public:
  
  static void call_default_constructor(JavaThread* thread, methodHandle method, Handle receiver, TRAPS);
 
  // 使用如下函数调用Java中一些特殊的方法,如类初始化方法<clinit>等
  // receiver表示方法的接收者,如A.main()调用中,A就是方法的接收者
  static void call_special(JavaValue* result, Klasshandle klass, Symbol* name,Symbol* signature, JavaCallArguments* args, TRAPS);
  static void call_special(JavaValue* result, Handle receiver, KlassHandle klass,Symbol* name, Symbol* signature, TRAPS); 
  static void call_special(JavaValue* result, Handle receiver, KlassHandle klass,Symbol* name, Symbol* signature, Handle arg1, TRAPS);
  static void call_special(JavaValue* result, Handle receiver, KlassHandle klass,Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
 
  // 使用如下函数调用动态分派的一些方法
  static void call_virtual(JavaValue* result, KlassHandle spec_klass, Symbol* name,Symbol* signature, JavaCallArguments* args, TRAPS);
  static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass,Symbol* name, Symbol* signature, TRAPS); 
  static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass,Symbol* name, Symbol* signature, Handle arg1, TRAPS);
  static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass,Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
 
  // 使用如下函数调用Java静态方法
  static void call_static(JavaValue* result, KlassHandle klass,Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);
   static void call_static(JavaValue* result, KlassHandle klass,Symbol* name, Symbol* signature, TRAPS);
  static void call_static(JavaValue* result, KlassHandle klass,Symbol* name, Symbol* signature, Handle arg1, TRAPS);
  static void call_static(JavaValue* result, KlassHandle klass,Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
 
  // 更低一层的接口,如上的一些函数可能会最终调用到如下这个函数
  static void call(JavaValue* result, methodHandle method, JavaCallArguments* args, TRAPS);
};


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

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

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

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

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


main()
-> //... 做一些参数检查
-> //... 开启新线程作为main线程,让它从JavaMain()函数开始执行;该线程等待main线程执行结束


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


JavaMain()
-> //... 找到指定的JVM
-> //... 加载并初始化JVM
-> //... 根据Main-Class指定的类名加载JavaMainClass
-> //... 在JavaMainClass类里找到名为"main"的方法,签名为"([Ljava/lang/String;)V",修饰符是public的静态方法
-> (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); // 通过JNI调用JavaMainClass.main()方法


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

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


// HotSpot VM里对JNI的CallStaticVoidMethod的实现。留意要传给Java方法的参数
// 以C的可变长度参数传入,这个函数将其收集打包为JNI_ArgumentPusherVaArg对象
-> jni_CallStaticVoidMethod()  
 
     // 这里进一步将要传给Java的参数转换为JavaCallArguments对象传下去    
     -> jni_invoke_static()  
       
        // 真正底层实现的开始。这个方法只是层皮,把JavaCalls::call_helper()
        // 用os::os_exception_wrapper()包装起来,目的是设置HotSpot VM的C++层面的异常处理
        -> JavaCalls::call()   
     
           -> JavaCalls::call_helper()
              -> //... 检查目标方法是否为空方法,是的话直接返回
              -> //... 检查目标方法是否“首次执行前就必须被编译”,是的话调用JIT编译器去编译目标方法
              -> //... 获取目标方法的解释模式入口from_interpreted_entry,下面将其称为entry_point
              -> //... 确保Java栈溢出检查机制正确启动
              -> //... 创建一个JavaCallWrapper,用于管理JNIHandleBlock的分配与释放,
                 // 以及在调用Java方法前后保存和恢复Java的frame pointer/stack pointer

              //... StubRoutines::call_stub()返回一个指向call stub的函数指针,
              // 紧接着调用这个call stub,传入前面获取的entry_point和要传给Java方法的参数等信息
              -> StubRoutines::call_stub()(...) 
                 // call stub是在VM初始化时生成的。对应的代码在
                 // StubGenerator::generate_call_stub()函数中
                 -> //... 把相关寄存器的状态调整到解释器所需的状态
                 -> //... 把要传给Java方法的参数从JavaCallArguments对象解包展开到解释模
                    // 式calling convention所要求的位置
                 -> //... 跳转到前面传入的entry_point,也就是目标方法的from_interpreted_entry

                    -> //... 在-Xcomp模式下,实际跳入的是i2c adapter stub,将解释模式calling convention
                       // 传入的参数挪到编译模式calling convention所要求的位置
                           -> //... 跳转到目标方法被JIT编译后的代码里,也就是跳到 nmethod 的 VEP 所指向的位置
                                -> //... 正式开始执行目标方法被JIT编译好的代码 <- 这里就是"main()方法的真正入口"

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

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

到此这篇关于关于Java虚拟机HotSpot的文章就介绍到这了,更多相关Java虚拟机HotSpot内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 关于Java虚拟机HotSpot

本文链接: https://lsjlt.com/news/156328.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 关于Java虚拟机HotSpot
    我们写的主类中的main()方法是如何被Java虚拟机调用到的?在Java类中的一些方法会被由C/C++编写的HotSpot虚拟机的C/C++函数调用,不过由于Java方法与C/C+...
    99+
    2024-04-02
  • 如何理解Java 虚拟机中HotSpot 虚拟机对象
    今天就跟大家聊聊有关如何理解Java 虚拟机中HotSpot 虚拟机对象,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、对象的创建对象的创建步骤:1. 类加载检查虚拟机遇到一条 n...
    99+
    2023-06-05
  • 关于VM虚拟机(Finalshell的连接问题)
    关于VM虚拟机(Finalshell的连接问题) 以下为出现的问题汇总 (1)关于IP地址 (2)关于连接失败(多次显示输入密码) (3)关于ufw无法使用 (4)关于防火墙 (5)关于Finalsh...
    99+
    2023-09-05
    服务器 linux 运维
  • 关于Vue虚拟dom问题
    目录一、什么是虚拟dom?二、为什么需要虚拟dom三、虚拟dom是如何转换为真实dom的四、模板和虚拟dom的关系一、什么是虚拟dom? 虚拟dom本质上就是一个普通的JS对象,用于...
    99+
    2024-04-02
  • 如何关闭虚拟机
    要关闭虚拟机,通常可以通过以下几种方法进行操作: 在虚拟机的操作系统中选择关机选项:可以在虚拟机的操作系统中选择关机选项,就像在物理计算机上一样。在Windows系统中,可以点击“开始”菜单,选择“关机”选项;在Linux系统中,可以使...
    99+
    2024-06-10
    virtualbox
  • 虚拟主机如何关闭
    关闭虚拟主机的具体步骤可能因所使用的虚拟主机软件而有所不同,下面是一般的方法:1. 登录虚拟主机管理控制面板或使用SSH登录虚拟主机...
    99+
    2023-08-22
    虚拟主机
  • 虚拟主机怎么关闭
    关闭虚拟主机的方法取决于你使用的虚拟主机服务提供商和你的操作系统。以下是一些常见的关闭虚拟主机的方法:1. 登录到你的虚拟主机控制面...
    99+
    2023-08-29
    虚拟主机
  • 虚拟主机关机怎么设置
    要关机虚拟主机,你需要登录到虚拟主机管理界面或者通过远程连接工具访问虚拟主机服务器。然后按照以下步骤设置关机:1. 打开管理界面或者...
    99+
    2023-08-30
    虚拟主机
  • 虚拟机关机命令是什么
    本文小编为大家详细介绍“虚拟机关机命令是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“虚拟机关机命令是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。虚拟机关机命令:关机命令的输入方法为:按下“Win+R...
    99+
    2023-06-30
  • Java虚拟机怎么用
    这篇文章主要讲解了“Java虚拟机怎么用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java虚拟机怎么用”吧!Java虚拟机Java虚拟机(JavaVirtualMachine)简称JVM...
    99+
    2023-06-17
  • 什么是Java虚拟机
    这篇文章主要介绍“什么是Java虚拟机”,在日常操作中,相信很多人在什么是Java虚拟机问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”什么是Java虚拟机”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!一、...
    99+
    2023-06-03
  • java虚拟机是什么
    Java虚拟机(Java Virtual Machine,简称JVM)是一种能够执行Java字节码的虚拟机,它是Java程序运行的核...
    99+
    2024-04-09
    java
  • Java虚拟机JVM类加载机制(从类文件到虚拟机)
    目录一、类加载机制简介二、类加载机制过程 2.1、加载(Load)2.2、连接(Linking)2.3、初始化(Initialize)三、类加载器Classloader&n...
    99+
    2024-04-02
  • VMware如何关闭ubuntu虚拟机
    VMware关闭ubuntu虚拟机的方法:1.打开VMware。2.展开“我的计算机”选项。3.右键“ubuntu”虚拟机。4.选择“电源”选项。5.点击“关机”即可。VMware关闭ubuntu虚拟机的方法:打开VMware主界面。在左侧...
    99+
    2024-04-02
  • nginx网站服务于虚拟主机
       Nginx网站服务于虚拟主机1:nginx的优点:稳定性高系统资源消耗低http并发量处理能力好可支持30000-50000个并发请求主要用于静态页面以线程为单位,一个进程对应多个...
    99+
    2024-04-02
  • 云服务器属于虚拟机吗
    云服务器(Cloud Server)是一种虚拟服务器,它是一种云计算服务,用于在互联网上提供虚拟的计算资源或存储资源。这些资源通常与客户端服务器(Server Center)集成,以便用户可以通过一个浏览器访问这些资源,而无需使用自己的本地...
    99+
    2023-10-26
    虚拟机 服务器
  • 关于Nginx中虚拟主机的一些冷门知识小结
    目录前言对线上配置的一个小疑问问题背景实测结果探索排查网络排查nginx总结前言 nginx的虚拟主机,不知道大家了解不。以前吧,如果在nginx上要反向代理多个服务,我一般是让ng...
    99+
    2024-04-02
  • eclipse打不开java虚拟机
    问题:打开eclipse,提示如下:解决方法:进入解压缩目录打开eclipse.ini配置文件(免费学习视频教程分享:java视频教程)内容如下:将参数改小修改后如图:保存后重启eclipse即可。相关文章教程推荐:java入门教程...
    99+
    2020-09-23
    java入门 eclipse 打不开 java 虚拟机
  • Java虚拟机之类加载
    目录一、类加载流程1.1 类加载条件1.2 加载1.3 验证1.4 准备1.5 解析1.6 初始化二、ClassLoader2.1 Class...
    99+
    2024-04-02
  • 如何优化Java虚拟机
    这篇文章将为大家详细讲解有关如何优化Java虚拟机,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。堆设置-Xmx3550m:设置JVM最大堆内存为3550M。-Xms3550m:设置JVM初始堆内存为355...
    99+
    2023-05-30
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作