Java 源代码怎么执行的

article/2025/8/18 20:26:21

许多 Java 虚拟机的执行引擎在执行 Java 代码的时候都是解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)混合运行。

大体流程为:

  • 编写 java 文件源码

  • 通过 javac 编译器将 java 源码编译为字节码流

  • 通过解释器解释执行字节码

  • 随着时间推移,即时编译器 (JIT) 介入,把越来越多的字节码编译成本地代码(机器码)执行

7db328cb7a7a19291028c2f2b5df5882.png

本文中无特殊说明,编译器指即时编译器,即在运行期间的编译。

二、解释器是怎么解释字节码流执行的

我们使用 javac 编译器编译完后会生成字节码流,这些字节码解释执行方式有 2 种。一种是基于栈的指令集,一种是基于寄存器的指令集。

比如一个 1 + 1 的计算。

基于栈的指令集时:

iconst_1    将 1 放入栈顶
iconst_1    将 1 放入栈顶
iadd        将栈顶的 2 个数相加后结果放入栈顶
istore_0    将相加的结果放入局部变量表

基于寄存器的指令集时:

mov eax,1 把 EAX 寄存器的值设为 1
add eax,1 再把这个值加 1 ,结果保存在了 EAX 寄存器

两套指令集的优缺点:

  • 基于栈的指令集优点是可移植,因为寄存器由硬件直接提供,受到硬件的约束。

  • 基于栈的指令集缺点理论上执行速度可能较慢,出栈入栈本身就涉及了大量的指令,而且栈是在内存中实现的。

实际中基于栈的指令集会被虚拟机优化,比如使用即时编译,常用操作映射到寄存器。

三、编译器是如何将字节码编译为本地机器码的

服务端编译器和客户端编译器的编译过程是有所差别。

对于客户端编译器来说:

它是一个相对简单快速的三段式编译器,主要的关注点在于局部性的优化,而放弃了许多耗时较长的全局优化手段。

在第一个阶段,一个平台独立的前端将字节码构造成一种高级中间代码表示(High-Level Intermediate Representation,HIR,即与目标机器指令集无关的中间表示)。HIR 使用静态单分配(Static Single Assignment,SSA)的形式来代表代码值,这可以使得一些在 HIR 的构造过程之中和之后进行的优化动作更容易实现。在此之前编译器已经会在字节码上完成一部分基础优化,如方法内联、常量传播等优化将会在字节码被构造成 HIR 之前完成。

在第二个阶段,一个平台相关的后端从 HIR 中产生低级中间代码表示(Low-Level Intermediate Representation,LIR,即与目标机器指令集相关的中间表示), 而在此之前会在 HIR 上完成另外一些优化,如空值检查消除、范围检查消除等,以便让 HIR 达到更高效的代码表示形式。

最后的阶段是在平台相关的后端使用线性扫描算法(Linear Scan Register Allocation)在 LIR 上分配寄存器,并在 LIR 上做窥孔(Peephole)优化,然后产生机器代码。

0bff95e36072c1fdfa062a7958cefae2.png

对于服务端编译器来说:

服务端编译器则是专门面向服务端的典型应用场景,并为服务端的性能配置针对性调整过的编译器,也是一个能容忍很高优化复杂度的高级编译器,几乎能达到 GNU C++ 编译器使用-O2 参数时的优化强度。它会执行大部分经典的优化动作,如:无用代码消除、循环展开、循环表达式外提、消除公共子表达式、常量传播、基本块重排序等, 还会实施一些与 Java 语言特性密切相关的优化技术,如范围检查消除、空值检查消除等。另外,还可能根据解释器或客户端编译器提供的性能监控信息,进行一些不稳定的预测性激进优化,如守护内联、分支频率预测等

服务端编译采用的寄存器分配器是一个全局图着色分配器,它可以充分利用某些处理器架构(如 RISC)上的大寄存器集合。以即时编译的标准来看,服务端编译器无疑是比较缓慢的,但它的编译速度依然远远超过传统的静态优化编译器, 而且它相对于客户端编译器编译输出的代码质量有很大提高, 可以大幅减少本地代码的执行时间,从而抵消掉额外的编译时间开销,所以也有很多非服务端的应用选择使用服务端模式的 HotSpot 虚拟机来运行。

四、为什么同时使用了解释器与编译器

解释器与编译器两者各有优势:

  • 当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即运行

  • 当程序启动后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码,这样可以减少解释器的中间损耗,获得更高的执行效率

  • 当程序运行环境中内存资源限制较大,可以使用解释执行节约内存(如部分嵌入式系统中和大部分的 JavaCard 应用中就只有解释器的存在)

  • 当程序运行环境中内存资源限制较小,可以使用编译执行来提升效率

五、提前编译器

前面仅说明了即时编译器,其实还有一种提前编译器,在我们编译字节码时直接将部分字节码生成本地代码。

JDK 9 引入了用于支持对 Class 文件和模块进行提前编译的工具 Jaotc,以减少程序的启动时间和到达全速性能的预热时间, 但由于这项功能必须针对特定物理机器和目标虚拟机的运行参数来使用,加之限制太多,Java 开发人员对此了解、使用普遍比较少。

提前编译器的两条分支:

  • 做与传统 C、C++ 编译器类似的,在程序运行之前把程序代码编译成机器码的静态翻译工作

  • 把原本即时编译器在运行时要做的编译工作提前做好并保存下来,下次运行到这些代码(比如公共库代码在被同一台机器其他 Java 进程使用)时直接把它加载进来使用

Android 安装包如果提前编译后,体积会变大。如果不提前编译启动运行可能会变慢。目前有一种优化手段就是空闲时间编译。

六、即时编译器的种类

JDK 10 前,HotSpot 拥有两款即时编译器,客户端即时编译器 C1。服务端即时编译器 C2。

从 JDK 10 起,HotSpot 新增一个 Graal 目标是代替服务端即时编译器 C2。

参考

  • Graal : a Java-Based JIT Compiler


http://chatgpt.dhexx.cn/article/FmuiFLYp.shtml

相关文章

Java基础-java程序代码截取字符串及判断字符串是否包含某个字符(串)

文章目录 有一串字符串,是一串拼接码,如何截取获取想要的字段?判断字符串是否包含某个字符(串)indexOf方法 有一串字符串,是一串拼接码,如何截取获取想要的字段? public class Inte…

编写高质量JAVA程序代码的建议

--------------------------------------------------------------------------------------------------- 前言:原著《改善JAVA程序的151个建议》有151个建议,我在拜读的过程根据自己的理解合并了其中的几个,并将每个建议的核心要义进行了一…

java简单记事本代码_简单记事本的java程序代码

展开全部 天啊, 冖_Na0 为什么会有62616964757a686964616fe4b893e5b19e31333262343038我编的记事本代码呢???呵呵……你肯定是“请教”过我的吧?? 呵呵……我自己编了一个,不过呢,没…

IntelliJ IDEA 写java程序代码

快捷键 注释 单行:选中代码 ctrl/ 再来一次就是取消 多行:选中代码 ctrlshift/ 再来一次就是取消 格式化 CtrlAltL

Android studio如何运行java程序代码

先看Java程序类 public class GGG {public static void main(String[] args) {System.out.println("我是java程序");} }就是这么简单的一个java程序,在之前studio上可以直接运行,但是像现在升级studio后就发现运行报错: 首先java…

怎样使用lntelliJ IDEA 编写Java程序代码?

下面使用IDEA创建一个Java程序,实现在控制台上打印HelloWorld!的功能,具体步骤如下。 1.创建Java项目 进入New Project界面后,单击New Project选项按钮创建新项目,弹出New Project对话框,如下图所示。 New Projeet 对…

Java学习代码合集

其实我学习java最根本的原因是:我是一个挺关注外在的人,虽然是个程序员,所以我很喜欢写出那些带有漂亮的界面的程序,因为C总是控制台,我不是很喜欢,在这份java代码合集中,我会记录自己学习Java界…

Java开发常用代码

一.获取UUID UUID uuid UUID.randomUUID(); String str uuid.toString(); // 去掉“-” String s str.replaceAll("-", "");二.获取固定格式的时间字符串 第一种 //获取当前时间对象 Date d new Date(); //创建日期格式化类对象,”yyyy/MM/dd HH:mm…

一个简单的Java例程

编写一个程序,输出如下: 1.在IDEA中新建一个helloworld类,注意Java中严格区分大小写 2.在文件中添加如下代码: 源代码如下: public class helloworld{public static void main(String[] args){System.out.println(&q…

Python3读取大文件的方法

Python3读取大文件的方法 1. 方法一:利用yield生成器2. 方法二:利用open()自带方法生成迭代对象,这个是一行一行的读取3. 二者的比较 1. 方法一:利用yield生成器 def readPart(filePath, size1024, encoding"utf-8"):w…

大文件处理(上传,下载)思考

文件处理一直都是前端人的心头病,如何控制好文件大小,文件太大上传不了,文件下载时间太长,tcp直接给断开了😱😱😱等 效果 为了方便大家有意义的学习,这里就先放效果图,如…

python拆分大文件(大文件分割)

python拆分大文件 前言实现过程实验结果 前言 在工作中常常会遇见一些大文件,由于内容太多,使用比如记事本、notePad等软件也打不开,无法查看内容,最好是将整个文件进行拆分,分开处理,现在处理的文件原本是…

如何快速地向服务器传大文件,大文件如何快速传输

在这个互联网时代,信息更新速度逐渐加快。用户在进行文件传输时,一定是希望既稳定又快速的,并且还能够保证安全。但是通常来讲,FTP文件传输并不能同时实现这三点的,特别是上传大文件时,FTP上传文件速度明显…

Linux - 怎么实现大文件传输

一 前言 博文《PageCache》中介绍了 PageCache 的优缺点,其实在处理大文件中 PageCache 作用反而没有那么好。所以本文介绍 Linux 是怎么处理大文件的。 二 起因 首先看一下一个 read() 系统调用流程发生了什么,如下图: 当调用 read 方法时…

如何快速传输大文件:4 种大文件传输有效的方法

文件大小正在爆炸式增长,随之而来的挑战是如何仍然以快速、安全的方式发送。从这个意义上说,弄清楚如何快速传输大文件似乎是一项几乎不可能完成的任务。随着工作流程不断适应数字化,这对于自由职业者、业余视频编辑、后期制作公司和广播公司…

win 10计算机查找大文件,教你如何在Win10系统中查找大文件?

Win10系统如何查找大文件?Win10系统内置有搜索功能,可以帮助用户快速找到所需文件,一般我们都是输入名称进行查找文件的。当然也有其他的搜索方式,比如按照文件大小搜索,相信大家比较少见吧。那么在Win10系统中该如何查…

如何进行大文件传输?

本文首发微信公众号:码上观世界 网络文件传输的应用场景很多,如网络聊天的点对点传输、文件同步网盘的上传与下载、文件上传到分布式文件存储器等,其传输速度主要受限于网络带宽、存储器大小、CPU处理速度以及磁盘读写速度,尤其是…

大文件分片上传

前言 前端进行文件大小分割 &#xff0c;按10M分割进行分片上传&#xff0c;使用的项目还是前面文档介绍的poi同一个项目 另一篇poi导出文章,使用的同一个项目 poi的使用和工具类&#xff08;一&#xff09; 开发 1、maven依赖 <!--文件分片上传使用到依赖 start --&g…

HTTP传输大文件

一 概述 早期网络传输的文件非常小&#xff0c;只是一些几K大小的文本和图片&#xff0c;随着网络技术的发展&#xff0c;传输的不仅有几M的图片&#xff0c;还有可以达到几G和几十G的视频。 在这些大文件传输的情况下&#xff0c;100M的光纤或者4G移动网络都会因为网络压力导致…

使用python读取大文件

读取文件时&#xff0c;如果文件过大&#xff0c;则一次读取全部内容到内存&#xff0c;容易造成内存不足&#xff0c;所以要对大文件进行批量的读取内容。 python读取大文件通常两种方法&#xff1a;第一种是利用yield生成器读取&#xff1b;第二种是&#xff1a;利用open()自…