JVM是什么?

JVM全称为Java Virtual Machine,直译为Java虚拟机,是运行Java字节码的虚拟计算机。它是Java平台的核心组件,负责将Java字节码转换为特定操作系统的机器指令并执行。JVM给予了内存管理、垃圾回收、安全性等机制,使得Java程序能够实现“一次编写,到处运行”的特性。

知识点字节码。只要符合JVM规范,任何语言生成的字节码都能在JVM上执行。例如Kotlin、Scala、Jython等知名语言,都可以凭借编译器生成JVM字节码并运行。就是:JVM只能运行Java代码吗?事实并非如此。JVM与Java语言并无必然联系,它实际运行的

JVM是怎么工作的?

JVM的完整执行流程如下:

  1. java命令启动JVM
  2. 创建引导类加载器并加载核心Java类库
  3. 创建主线程并执行main()方法
  4. 遇到新类时触发类加载过程
  5. 方法调用时创建栈帧,局部变量入栈
  6. 执行引擎解释或编译执行字节码
  7. 对象创建在堆内存分配空间
  8. 内存不足时触发垃圾回收
  9. 程序结束或异常终止时JVM退出
    在这里插入图片描述

主要步骤

一、类加载过程
  1. 加载(Loading)

    • 通过类加载器(ClassLoader)查找并加载类的二进制材料
    • 常见的类加载器包括:Bootstrap ClassLoader(加载Java核心类)、Extension ClassLoader(加载扩展类)、Application ClassLoader(加载应用程序类)和自定义ClassLoader
  2. 验证(Verification)

    • 确保加载的类符合JVM规范
    • 包括文件格式验证、元材料验证、字节码验证和符号引用验证
  3. 准备(Preparation)

    • 为类变量(static变量)分配内存并设置初始值(零值)
    • 例如:static int a = 10; 在此阶段a会被初始化为0而非10
  4. 解析(Resolution)

    • 将符号引用转换为直接引用
    • 涉及类、接口、字段和途径的解析
  5. 初始化(Initialization)

    • 执行类构造器()技巧
    • 为静态变量赋予程序员指定的初始值

关键知识点:Java类加载器和双亲委派模型
类加载过程

二、运行时资料区域管理

JVM在运行时管理多个内存区域:

  1. 程序计数器(Program Counter Register)

    • 线程私有
    • 记录当前线程执行的字节码行号
  2. Java虚拟机栈(JVM Stack)

    • 线程私有
    • 存储栈帧(Stack Frame),包含局部变量表、操作数栈、动态链接和方法返回地址
    • 每个技巧调用会创建一个新的栈帧
  3. 本地方法栈(Native Method Stack)

    • 线程私有
    • 为JVM使用到的本地方法服务
  4. Java堆(Heap)

    • 线程共享
    • 存储所有对象实例和数组
    • GC关键管理区域
    • 分为新生代(Eden、Survivor)和老年代
  5. 方法区(Method Area)

    • 线程共享
    • 存储类信息、常量、静态变量、即时编译器编译后的代码等
    • 在HotSpot JVM中也称为"永久代"(PermGen)或"元空间"(Metaspace)
      JVM内存模型
三、执行引擎
  1. 解释执行

    • 逐行解释字节码并执行
    • 启动快但执行效率低
  2. 即时编译(JIT)

    • 热点代码(Hot Spot)会被编译为本地机器码
    • 编译后执行效率高
    • HotSpot JVM采用分层编译策略

知识点:逃逸分析。逃逸分析是编译器优化的一部分,用于分析对象的动态作用域。编译器会检查对象的生命周期是否仅限于当前方法或线程,从而决定是否可以进行栈分配或锁消除等优化。
逃逸分析的结果直接影响以下优化:

  • 栈上分配(Stack Allocation):将未逃逸的对象分配在栈帧上,减少堆内存压力。
  • 标量替换(Scalar Replacement):将对象拆解为基本类型变量,消除对象头开销。
  • 同步消除(Lock Elision):若对象未逃逸到其他线程,移除不必要的同步锁。
  1. 垃圾收集(GC)
    • 自动内存管理机制
    • 常见GC算法:标记-清除、标记-整理、复制算法
    • 常见GC收集器:Serial、Parallel、CMS、G1等
  • Serial收集器
    单线程工作的收集器,采用标记-复制算法。在垃圾回收时会暂停所有用户线程(Stop-The-World),适合内存资源受限的客户端场景。新生代和老年代可分别启用-XX:+UseSerialGC参数。
  • Parallel Scavenge收集器
    多线程并行收集器,采用标记-复制算法(新生代)和标记-整理算法(老年代)。注重吞吐量优化,通过-XX:+UseParallelGC启用。提供-XX:MaxGCPauseMillis-XX:GCTimeRatio参数调整停顿时间与吞吐量。
  • ParNew收集器
    Parallel Scavenge的变种,需与CMS配合使用。通过-XX:+UseParNewGC启用,采用多线程标记-复制算法。在JDK9后被标记为废弃,推荐使用G1替代。
  • CMS收集器
    并发标记清除收集器,采用标记-清除算法。通过-XX:+UseConcMarkSweepGC启用,分为初始标记、并发标记、重新标记和并发清除四个阶段。缺点是会产生内存碎片,且无法处理浮动垃圾。
  • G1收集器
    面向服务端的收集器,采用标记-整理算法和Region内存布局。通过-XX:+UseG1GC启用,支持预测停顿时间模型。将堆划分为多个Region,通过Remembered Set避免全堆扫描。
  • ZGC收集器
    低延迟收集器,采用染色指针和读屏障技术。通过-XX:+UseZGC启用,支持TB级堆内存,停顿时间不超过10ms。基于Region设计,实现并发标记-整理算法。
四、本地方法接口

JNI(Java Native Interface)

  • 允许Java代码调用本地手段(C/C++编写)
  • 给予与操作系统交互的能力

JVM通过这种精细的工作流程,完成了Java的"一次编写,到处运行"的特性,同时保证了程序执行的效率和安全性。