1 Java的JRE
        作为一名Java程序员,你应该知道,java代码有很多种不同的运行方式。比如说在开发工具中运行,可以双击执行jar文件运行,也可以在命令行中运行,甚至可以在网页中运行。这些执行方式都离不开JRE,也就是java开发环境。
        实际上JRE仅包含运行java程序的必须组件,包括java虚拟机以及Java核心内库等。我们常说的JDK(Java开发工具包)同样包含JRE,并且还附带了一系列开发、诊断工具。

2 为什么Java要在虚拟机里运行?
        Java作为一门高级程序语言,它的语法非常复杂,抽象程度也很高。因此,直接在硬件上运行这种复杂的程序并不现实。(疑问:C++的就不复杂么??)所以在运行Java程序之前,我们需要对其进行一番转换。
        具体怎么转换呢?当前的主流思路是设计一个面向Java语言特性的虚拟机,并通过编译器将Java程序转换成该虚拟机所能识别的指令序列,也称Java字节码,之所以这么取名,是因为Java字节码指令的操作码(opcode)被固定为一个字节。
        Java虚拟机可以由硬件实现,但更为常见的是在各个现有平台上提供软件实现。这么做的意义在于,一旦一个程序被转换成Java字节码,那么它便可以在不同平台上的虚拟机实现里运行。一次编写,到处运行。
        虚拟机的另外一个好处是它带来了一个托管环境(Managed Runtime)。最熟知的功能——自动内存管理与垃圾回收。还提供了诸如数组越界、动态类型、安全权限等等的动态检测。

3 Java虚拟机具体是怎样运行Java字节码的?(标准JDK中的HotSpot虚拟机为例)

        从虚拟机视角来看,执行Java代码首先需要将它编译成class文件加载到虚拟机中。加载后的Java类会被存放在方法区(Method Area)中。实际运行时,虚拟机会执行方法区内的代码。
        Java虚拟机会将栈细分为面向Java方法的Java方法栈,面向本地方法(用C++写的native方法)的本地方法栈,以及存放各个线程执行位置的PC寄存器。
                                                                    图片说明
        在运行过程中,调入一个Java方法,Java虚拟机会在当前线程的Java方法栈中生成一个栈帧,存放局部变量以及字节码的操作数(疑问:什么叫字节码操作数)。这个栈帧大小提前计算好的,不要求在内存中连续分布。退出当前执行方法时,不管正常或是异常返回,Java虚拟机都会弹出当前线程的当前栈帧,并舍弃。
        从硬件视角看,Java字节码无法直接执行。因此Java虚拟机需要将它翻译成机器码。在HotSpot里面,上述翻译有两种形式。第一种是解释执行,逐条将字节码翻译成机器码并执行;第二种是即时编译(Just-In-Time compilation, JIT),将一个方法中包含的所有字节码编译成机器码后再执行。

                                                                            
        前者的优势在于无需等待编译,后者的优势在于实际运行速度更快。HotSpot默认采用混合模式。它会先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行及时编译。

4 Java虚拟机的运行效率怎样?
        HotSpot采用了多种技术来提升启动性能以及峰值性能,即时编译便是其中最重要的技术之一。理论上讲,即时编译后的Java程序执行效率,是可能超过C++的。这是因为与静态编译相比,即时编译拥有程序运行时信息,并且能够根据这个信息做出相应的优化。
        HotSpot内置了多个即时编译器:C1、C2和Graal。Graal是Java10正式引入的实验性即时编译器。C1又名Client编译器,面向的是对启动性能有要求的客户端GUI程序,优化手段相对简单,编译时间较短。C2又叫Server编译器,面向的是对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,编译时间较长,生成代码执行效率较高。
        为了不干扰应用的正常运行,HotSpot的即时编译是放在额外的编译线程中进行的。HotSpot会根据CPU的数量设置编译线程的数目,按1:2的比例配置给C1和C2。
        在计算资源充足的情况下,字节码的解释执行和编译执行可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。