通常,Java 语言虚拟机包含两大核心模块:执行器和内存管理器,这里的执行器就是专门用来执行字节码的。
在 Hotspot 里,解释器主要分为两大类:cpp 解释器和模板解释器。
每一个 Java 方法的栈里面都有一个模拟栈和一个变量表。在刚开始执行方法的时候,栈里的模拟栈是空的。
每一条字节码的语义都是由 Java 语言规范规定的,不管在什么平台上,模拟栈和变量表这两个数据结构都是相同的。本质上,字节码就是对模拟栈和变量表不断地进行操作。这种逐条取出字节码,逐条执行的方式被称为解释执行。对字节码进行解释执行的执行器叫做解释器。
JVM 在运行之初将 class 文件加载进内存,然后就开始解释执行。如果一个函数被执行多次,JVM 就会认为这个函数是一个热点 (hotspot) 函数,然后就将它翻译成机器码执行。
JIT 编译器能成功运行所依赖的两大核心机制,分别是:
- 申请可写可执行的内存区域,确保在运行器可以生成可执行的机器码;
- 基于性能采样的编译优化 (Profiling Guided Optimization, PGO),可以使得 JIT 编译器获得超过静态编译器的运行性能。
可写可执行的内存区域,这个机制是申请一块既有写权限又有执行权限的内存,然后把你要编译的 Java 方法,翻译成机器码,写入到这块内存里。当再需要调用原来的 Java 方法时,就转向调用这块内存。
让 JIT 编译器运行得好,我们只需要遵守一条原则:让程序行为可预测。因为 JIT 编译优化的基本假设是过去和未来,程序的运行规律基本一致,所以它基于过去的行为测试未来。如果它预测的未来和真实情况不一致,就会发生退优化。退优化的情况会对性能带来巨大的伤害,所以 JIT 有时也可能是一把双刃剑。
此文章为7月Day11学习笔记,内容来源于极客时间《编程高手必学的内存知识》