Java代码是怎么运行的?
前言
这个系列就属于理解Java虚拟机,写作过程边参考网上资料和书籍,一起加油。
正文
本文先做个简单的介绍,来大致了解一下Java虚拟机是干啥的。
Java作为我们非常熟悉的语言,Java代码有很多种不同的运行方式,比如可以在开发工具中运行,可以双击jar运行,这些执行方式不同,但是都离不开JRE,即Java运行时环境。
而实际上JRE就包含了运行Java程序的必须组件,包括Java虚拟机以及Java核心类库等等。
Java运行和C有什么区别
说起这个,大家都非常了解,C代码可以直接编译成给机器读的一个个字节,而Java却不可以。通常来说Java代码会先编译成Java字节码,而Java字节码再由Java虚拟机来读取,把Java字节码翻译成机器能识别的机器码。
而对于Android来说,便是会编译成Dex文件,再由手机系统的Java虚拟机来执行,那为什么Java要在虚拟机中运行呢 以及运行效率又如何,下面接着看。
为什么Java要在虚拟机中运行
Java虚拟机一般是在各个平台(如Windows、Linux)上提供软件实现,它是一个抽象的软件层,对上层Java代码来说它屏蔽了下层实现,所以这样做的意义是一旦一个程序被编译成Java字节码,它便可以在不同平台虚拟机上运行,这也就是Java的跨平台特性。
还有一个重要点是Java虚拟机提供了一个托管环境,这个环境可以替我们处理一些代码中冗长且容易出错的部分,比如最常见的就是Java虚拟机的内存管理和垃圾回收,不用像C++等语言需要自己处理。
Java虚拟机运行字节码
既然我们了解了,Java虚拟机是用来运行Java字节码的,那是如何执行的呢
虚拟机角度
从虚拟机角度来说,这里先按照标准JDK中的HotSpot来举例,执行Java代码首先要把.class文件加载到Java虚拟机中,而Java中都是以类形式存在,所以加载后的Java类会被存放在方法区中,实际运行时,虚拟机会执行方法区中的代码。
你会看到这里有个名称叫做方法区,Java虚拟机会在内存中划分出堆和栈来存储运行时数据,而栈又被细分为Java方法的Java方法栈以及本地方法的本地方法栈,以及存放各个线程执行位置的PC寄存器。
大致结构如下:
关于这里为什么这样分,其实是非常有道理的,后面具体说内存模型时再细说。
在虚拟机运行过程中,每当调用一个Java方法,Java虚拟机会在当前线程的Java方法栈中生成一个栈帧,用于存放局部变量以及字节码的操作树,当退出当前执行的方法时,就会弹出这个栈帧。
硬件角度
Java字节码无法直接执行,需要由Java虚拟机来翻译,而在HotSpot中,翻译分为2种:一种是解释执行,即逐条将字节码翻译成机器码并执行,第二种是即时编译,即将一个方法种包含的所有字节码编译成机器码再执行。
优势就是解释执行无需等待编译,而即时编译在实际运行时会更快,因为是翻译过的。HotSpot默认是采用混合模式。
运行效率
前面说了2种翻译方式,这就要理解透彻了,这直接就影响Java虚拟机的运行效率。我们可以这样理解,解释执行占用内存少,而即时编译占用内存多但执行的快,所以Java虚拟机采用合理的机制,来让对于占据大部分的不常用代码我们无需耗费时间将其编译成机器码,而是采用解释执行,而对于小部分的热点代码,可以将其翻译成机器码,而从加快运行速度。
对于Java后台来说,一般代码都是长时间运行,所以Java虚拟机使用即时编译器的话,Java虚拟机会保存程序运行的信息,来动态获取哪些代码是热点代码需要即时编译来加快速度而同时又不占用更多的内存,从而得到一个平衡点。
对于Android程序来说,因为要经常启动APP,而APP运行又不是很久,所以在Android 5.0采用了带AOT(ahead of time compilation)这种模式,即线下将Java字节码编译成机器码,在运行时更快,在啥时候编译呢,就在安装APP的时候,这样的话就会稍微增加一些安装时间,但是在启动APP时会更快。
总结
本文只是一个引子,我们需要理解为啥要Java虚拟机,以及Java虚拟机在内存中分了几个部分,而优秀的Java虚拟机又会结合不同的翻译方式来到达一个性能峰值点。
转载自:https://juejin.cn/post/7081639125480112135