JVMJava Virtual MachineJava 虚拟机是 Java 程序的运行环境。它最大的贡献是实现了“一次编写到处运行”跨平台因为 JVM 屏蔽了底层操作系统的差异Java 代码被编译成字节码.class文件由不同平台上的 JVM 解释执行。JVM 的基本原理非常庞大但核心可以归纳为三大模块类加载机制、运行时数据区内存模型、垃圾回收机制。以下是 JVM 基本原理的全面解析一、 JVM 整体架构JVM 的生命周期和核心组件可以抽象为以下四个部分类加载器ClassLoader负责将.class文件加载到内存中并生成代表该类的java.lang.Class对象。运行时数据区Runtime Data AreasJVM 的内存模型程序运行时的数据存放地。执行引擎Execution Engine负责执行字节码。包含解释器逐行解释执行和 JIT 编译器将热点代码编译成本地机器码以提高效率。本地方法接口Native Interface用于调用底层 C/C 编写的本地方法Native 方法。二、 运行时数据区内存模型—— 核心重点JVM 在运行 Java 程序时会将内存划分为不同的区域。根据线程共享的关系可以分为“线程私有”和“线程共享”两大类。1. 线程私有区域随线程生灭不需要 GC程序计数器Program Counter Register作用记录当前线程所执行的字节码的行号指示器。如果执行的是 Java 方法记录的是正在执行的虚拟机字节码指令地址如果是 Native 方法则为空。特点唯一不会发生OutOfMemoryError的区域。虚拟机栈VM Stack作用描述 Java方法执行的内存模型。每个方法被执行时都会创建一个“栈帧Stack Frame”用于存储局部变量表、操作数栈、动态链接、方法出口地址等信息。方法执行完毕栈帧出栈。异常如果线程请求的栈深度大于虚拟机允许的深度抛出StackOverflowError如果允许动态扩展但无法申请到足够内存抛出OutOfMemoryError。本地方法栈Native Method Stack作用与虚拟机栈类似只不过它是为 JVM 调用Native 方法如 C/C 代码服务的。HotSpot 虚拟机直接将其与虚拟机栈合二为一。2. 线程共享区域随虚拟机生灭GC 的主战场堆Heap作用JVM 中最大的一块内存区域所有对象实例和数组都在这里分配内存。分代模型为了提高 GC 效率堆通常被分为新生代Young Generation和老年代Old Generation。新生代朝生夕死存活率低。细分为 Eden 区、Survivor 0 区、Survivor 1 区比例默认 8:1:1。老年代存放长期存活的对象如经过多次 Minor GC 依然存活的对象或大对象。方法区Method Area作用存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。JDK 8 的变化重要在 JDK 8 之前方法区被称为“永久代PermGen”使用的是 JVM 自己的内存JDK 8 开始取消了永久代改为“元空间Metaspace”使用的是操作系统的本地内存Native Memory大大减少了 OOM 的发生。直接内存Direct Memory作用不属于 JVM 运行时数据区但频繁使用。NIONew IO允许直接使用 Native 函数库直接分配堆外内存然后通过一个存储在堆中的DirectByteBuffer对象作为引用进行操作。避免了在 Java 堆和 Native 堆之间来回复制数据提高了 I/O 性能。三、 类加载机制类加载是指把.class文件中的二进制数据读入内存将其放入方法区中并在堆区创建一个代表该类的java.lang.Class对象。1. 类加载的 5 个过程加载Loading通过类的全限定名获取定义此类的二进制字节流转化为方法区的运行时数据结构生成 Class 对象。验证Verification确保 Class 文件的字节流包含的信息符合当前虚拟机的要求保证安全性如文件格式验证、元数据验证、字节码验证。准备Preparation为类的静态变量分配内存并设置初始值如int初始值为 0而不是代码中赋的值。解析Resolution将常量池内的符号引用名字替换为直接引用内存地址/指针。初始化Initialization执行类构造器clinit方法的过程真正执行代码中的静态变量赋值和静态代码块。这是类加载的最后一步。2. 双亲委派模型Parent Delegation Model概念类加载器之间具有层次关系Bootstrap - Extension - Application - Custom。当一个类加载器收到类加载请求时它自己不会先去加载而是将请求委派给父类加载器层层向上直到最顶层的 Bootstrap 类加载器。只有当父加载器无法加载时子加载器才会尝试自己加载。作用避免类的重复加载。保护程序安全防止用户自己编写一个java.lang.Object类来替换系统的核心 API沙箱安全机制。四、 垃圾回收Garbage Collection, GCGC 主要关注三件事哪些内存需要回收什么时候回收如何回收主要针对堆和方法区。1. 如何判断对象已死引用计数法给对象添加引用计数器被引用时 1引用失效时 -1。为 0 就是垃圾。缺点无法解决循环引用问题Java 不使用。可达性分析算法Java 采用通过一系列称为“GC Roots”的对象作为起始点向下搜索引用链。如果一个对象到 GC Roots 没有任何引用链相连则证明此对象是不可用的可以被回收。常见的 GC Roots虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中 JNI 引用的对象。2. 垃圾回收算法标记-清除Mark-Sweep先标记出所有需要回收的对象然后统一清除。缺点产生大量内存碎片。复制算法Copying将内存分为两半每次只使用其中一半。GC 时将存活对象复制到另一半然后清空当前使用的一半。优点无碎片高效。缺点浪费一半内存。适用于新生代因为 98% 的对象朝生夕死。标记-整理Mark-Compact先标记然后让所有存活对象向一端移动直接清理掉边界外的内存。优点无碎片。适用于老年代。分代收集算法当前商业虚拟机的 GC 都采用这种思想。根据对象存活周期的不同将堆分为新生代和老年代分别采用复制算法和标记-整理/清除算法。3. 常见垃圾收集器Serial / Serial Old单线程收集器简单高效适用于客户端模式。ParNewSerial 的多线程版本常与 CMS 配合使用。Parallel Scavenge / Parallel Old多线程关注吞吐量运行用户代码的时间 / 总时间JDK 8 默认。CMSConcurrent Mark Sweep以获取最短回收停顿时间为目标基于标记-清除算法运行在老年代。缺点对 CPU 敏感、会产生碎片、浮动垃圾。G1Garbage-First面向全堆将堆划分为多个大小相等的独立区域Region不再物理分代。可预测的停顿时间模型JDK 9 之后的默认收集器。ZGC / Shenandoah新一代超低延迟收集器停顿时间通常在 10ms 以内采用染色指针和读屏障技术支持 TB 级别的堆。五、 执行引擎与 JIT 编译字节码是跨平台的但机器只能执行机器码。执行引擎负责将字节码转换为机器码。解释器Interpreter逐行将字节码解释为机器码执行。启动快但执行慢。JIT 编译器Just-In-Time Compiler在运行时将热点代码频繁执行的代码编译成本地机器码并缓存起来。执行快但编译需要时间。代码优化技术JIT 在编译时会进行优化如方法内联将方法体直接嵌入到调用处减少方法调用栈的开销。逃逸分析分析对象的作用域判断对象是否“逃逸”出方法或线程。如果没有逃逸可以直接在栈上分配内存方法结束自动销毁减轻 GC 压力或者进行锁消除。六、 总结与面试建议如果你要准备 JVM 相关的面试建议按照以下优先级掌握必考基础JVM 内存模型堆、栈、方法区/元空间的区别哪些是线程私有/共享、GC Roots、双亲委派模型。进阶原理类的加载过程、垃圾回收算法、分代收集思想、如何排查 OOM 和 CPU 飙高问题jstat, jmap, jstack, Arthas 等工具。高阶调优与前沿G1 和 ZGC 的核心原理与区别、JVM 参数调优实战、逃逸分析等 JIT 优化技术。理解 JVM 原理不仅能帮你应对面试更重要的是能让你写出更省内存、执行更快、更少 Bug的高质量 Java 代码。