JVM运行时系统是以运行时数据区为中枢、多组件按需协同的动态系统;解释器与JIT共存于执行引擎并按热度分级执行,GC通过安全点与执行引擎协作,类加载子系统单向供给元数据并触发初始化,GC与类加载间接关联于元空间生命周期。
JVM 运行时系统不是线性流水线,而是一个以运行时数据区为中枢、多组件按需协同的动态系统。解释器、JIT 编译器、GC 和类加载子系统并不直接“调用”彼此,而是通过共享内存区域(主要是方法区、堆、程序计数器)和状态标记进行松耦合交互。
解释器与JIT编译器:共存于执行引擎,按热度分级执行两者同属执行引擎子系统,共享字节码输入和运行时数据区上下文:解释器负责首次执行——逐条读取字节码,翻译成机器指令并执行,启动快但效率低;
JIT编译器不立即工作,而是由热点探测器(如HotSpot的MethodData)持续监控方法调用频次与循环次数;
当某方法被判定为“热点”(例如调用超10000次,默认阈值),JIT将该方法的字节码编译为本地机器码,并存入代码缓存(CodeCache);
后续调用直接跳转至编译后的本地代码,绕过解释器;若代码被频繁重定义(如反射修改),JIT还会触发去优化(deoptimization),退回解释执行。
GC与执行引擎:读写隔离 + 安全点协作GC不主动“调用”解释器或JIT,但执行必须与它们协调:GC主要回收堆和元空间(原永久代),而解释器/JIT的执行逻辑依赖堆中对象引用、方法区中的类元数据;
所有GC停顿(Stop-The-World)都发生在安全点(Safepoint)
:此时每个线程必须暂停在可识别的位置(如方法返回处、循环边界),确保对象图处于一致状态;
解释器在字节码分发循环中内置安全点轮询;JIT编译后的代码会在特定位置插入轮询指令(如测试全局标志位);
类加载子系统在初始化类或解析符号引用时也可能触发GC(如分配Class对象或常量池项),此时同样需进入安全点。
类加载子系统与执行引擎:单向触发 + 元数据供给类加载是执行的前提,但加载完成即“交付”,后续无持续通信:类加载子系统将.class字节码解析后,在方法区生成运行时常量池、字段/方法表、类的静态变量等结构,并在堆中创建对应的java.lang.Class实例;
解释器或JIT在执行过程中遇到未解析的符号引用(如第一次调用某个方法),会触发解析阶段(Linking中的Resolution),由执行引擎向类加载子系统发起“解析请求”,实际由当前类加载器完成符号引用→直接引用的转换;
类初始化(方法执行)由执行引擎主动触发——当首次主动使用类的静态变量、静态方法或构造器时,执行引擎检查类是否已初始化,未初始化则挂起当前线程,委托类加载子系统完成初始化流程。
GC与类加载子系统:间接关联,聚焦元空间生命周期二者交互集中在方法区(HotSpot中为元空间)的资源管理上:类加载子系统每成功加载一个类,就在元空间分配内存存储其元数据(类名、方法签名、注解等);
当类不再被任何对象引用,且满足卸载条件(如对应ClassLoader被回收),GC会将该类的元数据从元空间回收;
元空间本身使用本地内存(而非Java堆),其扩容/收缩由GC策略间接驱动(如G1或ZGC会监控元空间使用率,在Full GC或并发周期中触发卸载);
类卸载不是GC的必选项,仅在内存压力大且满足严格条件时发生,因此大量动态生成类(如Spring CGLIB、Groovy脚本)易引发元空间OOM。
