文章目录
- Java代码是怎么运行的?
- 几个为什么
- 为什么在虚拟机中运行?
- Java 虚拟机具体又是怎样运行 Java 代码的呢?
- Java虚拟机的运行效率怎么样?
- 总结
Java代码是怎么运行的?
来来来,运行个"Hello word !“告诉我是怎么运行的?
看似这个很简单的问题,就像1+1=2一样,很是轻蔑,听老师讲当时陈景润研究了好长时间,也有说是罗素证明出来,还有说从来没有证明出来过,但不管怎么样这些由它去吧,运行"Hello word !” 就是麻雀虽小五脏俱全,一个简单流程,包括了所有的,我们继续回归Java是怎么运行的,我个人理解是这样的
编写代码–>JDK编译成字节码–>JVM解析字节码–>机器指令–>输出设备展示。
跟郑老师学习深入拆解JVM中,郑老师的开篇又是一个小故事,幽默但不偏离主题,说到海关问一个教授的问题,来引出对Java是什么运行的提问。
Java有很多种运行方式
- 在工具中运行
- 双击jar文件运行
- 命令的方式运行
- 甚至在网页中运行
均需满足有JRE的Java运行环境
再说说JRE
JRE(Java Runtime Environment)Java运行环境,包括Java程序的必须组件、Java虚拟机、以及核心类库等。JDK(Java Development Kit)Java开发工具和JRE的区别是,JDK包括JRE,JDK中还有开发、诊断工具,就是JDK包含JRE。
其中还提到的了C++的运行方式,C++是不需要额外的运行时,直接往往把代码直接编译成CPU所理解的代码,也就是机器码,所以C++没有很好的快平台性,Java就有跨平台性,因为JVM搞定了环境系统的差异。
几个为什么
- 为什么 Java 要在虚拟机中运行呢?
- Java 虚拟机具体又是怎样运行 Java 代码的呢?
- 它的运行效率又如何呢?
Java是门高级语言,语法非常复杂,抽象程度很高,直接在机器上运行肯定是不可能的,运行起来是要过一番转换的。
语言的演变
二进制–>汇编语言–>高级语言
为什么在虚拟机中运行?
怎么转换呢?
Java源文件–>字节码–>在JVM上运行
因为字节码是底层的,是指令的操作码,被固定为一个字节,这也是字节码的由来。
JVM也可以由硬件实现,但更常见的是window、Linux、mac系统上提供软件实现的。这么做的好处是编译成字节码就直接在JVM上运行,也就是我们经常说的"一次编译,到处运行"。
JVM另外的一个好处是它带来了一个托管环境(Managed Runtime),能处理一些冗长容易出错的部分,其中最广为人知的就是内存管理和垃圾回收,这部分催生了一波垃圾回收调优的业务。嘿嘿,没有伤害就没有买卖。
托管环境还提供了诸如,数据越界、动态类型、安全权限等等的动态检测,使得我们免于书写这些无关的业务逻辑代码。都封装好了,我们只管用就好了,这让我想起来超市的模式,超市就是托管模式,商贩们提供卖东西的服务,就是正常的方法,其他的经营由超市管理人员来管理,商贩拿个摊位费。感觉就是编写代码的人,只管买卖东西,啥垃圾清扫啥的不需要顾客问,费用包含在商品里了,比喻虽然不恰当,但有相似之处 。
咱不熟悉X86咱也不明白这个如果你熟悉 X86 的话,你会发现这和段式内存管理中的代码段类似。而且,Java 虚拟机同样也在内存中划分出堆和栈来存储运行时数据。
Java 虚拟机还将栈细分为面向Java方法的Java方法栈,面向本地方法的本地方法栈(用C++ 写的native方法),存放线程执行位置信息的地方是PC寄存器,寄存器是存放指令集的数据。
在运行过程中,每当调用进入一个Java方法,虚拟机会在当前线程的Java方法栈中生成一个栈帧,用于存局部变量以及字节码的操作数,这个栈帧的大小是提前分配好的,而且Java虚拟机不要求栈帧在内存空间连续分布。
Java 虚拟机具体又是怎样运行 Java 代码的呢?
退出执行方法
不论正常返回,还是异常返回,Java虚拟机均会弹出当前线程的当前栈帧,并将之舍弃。
字节码执行
Java字节码是无法直接执行,Java虚拟机需要将字节码翻译成机器码。
怎么编译?
以HotSpot为例
- 解释执行,即逐条将字节码翻译成机器码并执行;
- 即时编译(Just-In-Time compilation ,JIT),将一个方法总包含字节码编译成机器码后再执行。
两者的差别对比,前者的优势在于无需等待编译,而后者的优势在于速度更快。HotSpot默认采用的是混合编译的模式,综合了解释执行和即时编译两者的优点。先解释执行字节码,然后复制执行热点代码,以方法为单位进行即时编译。优点中西合璧的感觉,取其精华去其糟粕。
Java虚拟机的运行效率怎么样?
HotSpot采用多种技术来提升启动性能以及峰值性能,即时编译就是其中一种提升性能的方式。
二八定律也可以称为帕累托法则,事物的主要结果只取决于一小部分因素,这里也试用,20%的代码,占据了80%的计算资源。对大部分不常用的代码使用解释执行的方式进行,对于仅占据小部分热点的热点代码可以采用编译成机器码,以达到理想的运行速度。
理论上讲,即时编译后的 Java 程序的执行效率,是可能超过 C++ 程序的。这是因为与静态编译相比,即时编译拥有程序的运行时信息,并且能够根据这个信息做出相应的优化。
据说交易系统的实现和编写都是使用C++,相对的C++速度较快,但最近出现了GoLang,据说很多大厂都将Java代码的功能使用Go进行重构,作为Java程序员心里老慌了,但学好JVM进行优化,我们将代码优化,可能依然是Go取代不了的。
虚拟方法居然是面向对象语言多态性的,对于一个虚方法调用,尽管它有很多个目标方法,但在实际运行过程中它可能只调用其中一个,这个信息便可以被即时编译器所利用,来规避虚方法调用的开销,从而达到比静态编译的 C++ 程序更高的性能。
这点是之前从来没有接触过的,听君一席话胜读十年书的感觉油然而生。
HotSpot内置编译器
- C1 Client编译器,面向的是对启动性能有要求的客户端GUI程序,采用的优化手段简单,编译时间较短
- C2 Server 编译器,面向的是对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但同时生成代码的执行效率较高
- Graal Java 10中正式引入的实验性即时编译器
从Java 7开始,HotSpot默认采用分层编译方式:热点方法首先会被C1编译,而后的热点方法中的热点会进一步的被C2编译。
这编译来编译去的,影响正常应用程序运行吗?HotSpot的即时编译是放在额外的编译线程中进行的,这当然不影响应用进程。HotSpot会根据CPU的数量设置编译线程的数目,并按照1:2的比例给到C1和C2编译器。
如果计算资源充足,字节码解释执行和即时编译同时进行,编译后的机器码会在下次调用该方法时启用,来替换原来的解释行。
总结
java的运行经过几个阶段,编译、运行,运行在虚拟机上的优点是"一次编译,导出运行",的便捷性,虚拟机不仅提供字节码的运行环境和提供代码托管的环境,处理一些冗长且容易报错的事务,内存管理等。
Java虚拟机分为5个区,方法区、堆、PC寄存器、Java方法栈、本地方法栈,class字节码文件首先加载到虚拟机的方法区,才能在JVM中运行。
为了执行效率,HotSpot采用的混合执行的策略,首先采用的解释执行的方式,后续热点代码将采用即时编译进行以方法为单位的。
此文章为 4 月 Day18 学习笔记,内容来源于极客时间链接: 《深入拆解 Java 虚拟机》如果想学Java虚拟机的强烈推荐该课程!