1.Java 虚拟机(JVM)

  • JVM 基础知识
    • 1. JVM 是什么?
      • JVM 是 Java 虚拟机的缩写,是 Java 程序运行的核心环境。它负责将 Java 字节码(.class 文件)解释或编译为机器码,并在不同平台上运行 。JVM 是 Java “一次编写,到处运行”的关键。
    • 2. JVM 的核心功能
      • (1)内存管理 :自动分配和回收内存(堆、栈、方法区等)。
      • (2)字节码执行 :通过解释器或即时编译器(JIT)执行字节码。
      • (3)垃圾回收 :自动回收不再使用的对象。
      • (4)多线程支持 :管理线程的创建、同步和销毁。
    • 3. JVM 的组成部分
      • (1)类加载器(Class Loader): 负责加载 Java 类文件,把类加载到内存中。
      • (2)运行时数据区(Runtime Data Areas) :这部分内存包含多个区域,像(方法区、堆、栈等),每个区域存储不同的数据,支持程序的运行。
      • (3)执行引擎(Execution Engine) :它执行程序中的指令,确保代码被按顺序执行。
      • (4)本地方法接口(JNI)
      • (5)本地方法库(Native Libraries)
  • JVM 运行时内存区域的划分
    • 一、JVM 运行时内存区域的划分
      • 运行时数据区域

        • 描述
      • 方法区(Method Area)

      • 堆(Heap)

      • 栈(Stack)

        • Java 虚拟机栈(Java Virtual Machine Stack)
        • 本地方法栈(Native Method Stack)
      • 程序计数器(Program Counter Register)

    • 二、各内存区域的详细说明
      • 1、方法区(Method Area)
        • 作用
          • 存储已被 JVM 加载的 类信息常量静态变量 、即编译器编译后的.Class代码等
          • 线程共享 的内存区域。
        • 具体内容
          • 类信息:类的全限定名、父类、接口、字段、方法等。
          • 运行时常量池:存放编译期生成的字面量和符号引用。
        • 注意事项
          • JDK 8 之前,方法区被称为永久代(PermGen)。
          • JDK 8 及之后,方法区被元空间(Metaspace)取代,元空间使用本地内存(Native Memory),不再受 JVM 堆内存限制。
        • 异常
          • 如果方法区无法满足内存分配需求,会抛出 OutOfMemoryError
      • 2、堆(Heap)
        • 作用
          • 存储 对象实例数组
          • 是 JVM 中最大的一块内存区域,也是垃圾回收的主要区域。
          • 线程共享 的内存区域。
        • 划分
          • 新生代(Young Generation)
            • 存放新创建的对象。
            • 分为 Eden 区、Survivor 区(From 和 To)。
            • 大多数对象在新生代被回收(Minor GC)。
          • 老年代(Old Generation)
            • 存放长期存活的对象。
            • 当对象在新生代经过多次 GC 后仍然存活,会被晋升到老年代。
            • 老年代的垃圾回收称为 Full GC。
        • 异常
          • 如果堆内存不足,会抛出 OutOfMemoryError
      • 3、栈(Stack)
        • Java 虚拟机栈(Java Virtual Machine Stack)
          • 栈结构

            • 栈结构
          • 作用

            • 每个线程 私有的 内存区域,用于 存储 方法调用的栈帧 (Stack Frame)。
            • 栈帧包括:局部变量表 、操作数栈、动态链接、方法返回地址。
          • 栈结构

            • (1)局部变量表

              • 主要存储方法参数和局部变量。
                • 编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)
                  • 在这里插入图片描述
            • (2)操作数栈

              • 主要作为方法调用的中转站使用,用于存储计算过程中的临时数据。
            • (3)动态链接

              • 主要服务一个方法需要调用其他方法的场景。
            • (4)方法返回地址

          • 异常

            • Java 虚拟机规范允许 Java虚拟机栈的大小是动态的或者是固定不变的
            • (1)如果栈深度超过限制,会抛出 StackOverflowError
            • (2)如果栈无法动态扩展,会抛出 OutOfMemoryError

        每个线程 有一个私有的栈,用来存储 局部变量方法调用的状态

        • 本地方法栈(Native Method Stack)
          • 作用
            • 本地方法 (Native Method)服务。通常用于操作系统底层代码。
            • 与 Java 虚拟机栈类似,但用于执行本地方法。本地方法是使用 C 语言实现的。
          • 异常
            • Java 虚拟机规范允许 Java虚拟机栈的大小是动态的或者是固定不变的
            • (1)如果栈深度超过限制,会抛出 StackOverflowError
            • (2)如果栈无法动态扩展,会抛出 OutOfMemoryError
      • 4、 程序计数器(Program Counter Register)
        • 作用
          • 记录当前线程执行的字节码 指令地址
          • 如果执行的是本地方法,则计数器值为空(Undefined)。
        • 特点
          • 线程私有,每个线程都有一个独立的程序计数器。
          • 是 JVM 中唯一不会抛出 OutOfMemoryError 的区域。
      • 6. 直接内存 (Direct Memory)
        • 直接内存是 JVM 之外的一块内存区域,通常通过 NIO(New Input/Output) 来分配,允许 Java 程序 直接操作内存 ,从而提高 I/O 性能。
        • 直接内存不由 JVM 管理,它是通过 操作系统提供的接口 来分配和回收的。
        • 直接内存通过 java.nio. ByteBuffer 类来管理,它允许 JVM 直接访问操作系统的物理内存。
        • ByteBuffer buffer = ByteBuffer.allocateDirect(1024); // 分配直接内存

    • 三、内存区域的线程共享性
      • 线程共享的区域
        • 方法区
      • 线程私有的区域
        • Java 虚拟机栈
        • 本地方法栈
        • 程序计数器
    • 四、内存区域的异常
      • **OutOfMemoryError**
        • 当 JVM 无法分配足够的内存时抛出。
        • 常见场景:
          • 堆内存不足(对象过多)。
          • 方法区(元空间)内存不足(加载的类过多)。
          • 栈内存不足(栈帧过多)。
      • **StackOverflowError**
        • 当栈深度超过限制时抛出。
        • 常见场景:递归调用过深。
    • 五、内存区域的调优
      • 1、方法区(元空间)
        • 参数:
          • -XX:MetaspaceSize :初始元空间大小。
            • X:表示这是一个 JVM 的标准参数。-XX:表示这是一个 JVM 的扩展参数(非标准参数)
          • -XX:MaxMetaspaceSize :最大元空间大小。
          • -XX:+PrintGCDetails ::打印垃圾回收详细信息,监控元空间的使用情况。
          • 示例启动命令: java -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=512M MyProject
      • 2、堆
        • 参数:
          • -Xms :初始堆大小。
          • -Xmx :最大堆大小。
          • -Xmn :新生代大小。
      • 3、栈
        • 参数:
          • -Xss :设置每个线程的栈大小。
  • 字节码与执行引擎
    • 一、 JVM 字节码
      • 字节码是 Java 源代码编译后的中间表示形式,它是一种平台无关的指令集,由 JVM 解释或编译执行。
      • 字节码的特点:
        • 紧凑性 :字节码是一种紧凑的二进制格式,比源代码更小。
        • 可移植性 :字节码文件( .class )可以在不同操作系统和硬件架构上运行。
        • 平台无关性 :字节码可以在任何安装了 JVM 的平台上运行。
      • 字节码的组成:
        • (1)魔数(Magic Number) :.class 文件的前 4 个字节是固定的 0xCAFEBABE,用于标识这是一个合法的字节码文件。
        • (2)版本号 :标识字节码文件的版本(主版本号和次版本号)。
        • (3)常量池(Constant Pool) :存储字符串常量、类名、方法名、字段名等符号引用
        • (4)访问标志 :描述类或接口的访问权限(如 public、final 等)。
        • (5)类索引、父类索引、接口索引 :描述类的继承关系。
        • (6)字段表 :描述类中声明的字段。
        • (7)方法表 :描述类中声明的方法,包括方法的字节码指令。
        • (7)属性表 :存储额外的信息(如源代码行号、局部变量表等)。
      • 字节码指令集:
        • 字节码指令是 JVM 的基本操作指令,例如:
        • 加载和存储指令 :如 iload (加载整数)、 istore (存储整数)。
        • 算术指令 :如 iadd (整数加法)、 isub (整数减法)。
        • 类型转换指令 :如 i2l (将整数转换为长整数)。
        • 对象操作指令 :如 new (创建对象)、 getfield (获取字段值)。
        • 控制转移指令 :如 ifeq (如果等于 0 则跳转)、 goto (无条件跳转)。
        • 方法调用指令 :如 invokevirtual (调用实例方法)、 invokestatic (调用静态方法)。
    • 二、JVM 执行引擎
      • 执行引擎是 JVM 的核心组件,负责解释或编译字节码并执行。它的主要任务是将字节码转换为机器码并运行。
      • 2.1 执行引擎的工作模式:
        • 2.1.1 解释执行
          • 执行引擎逐条解释字节码指令并执行。
          • 优点:启动速度快,适合短生命周期的程序。
          • 缺点:执行速度较慢,因为每次运行都需要解释。
        • 2.1.2 即时编译(JIT,Just-In-Time Compilation)
          • 执行引擎将热点代码(频繁执行的代码)编译为本地机器码,并缓存起来。
          • 优点:执行速度快,适合长时间运行的程序。
          • 缺点:启动速度较慢,因为需要编译。
        • 2.1.3 混合模式
          • JVM 默认使用混合模式,结合了解释执行和即时编译的优点。
          • 初始阶段使用解释执行,随后对热点代码进行即时编译。
      • 2.2 执行引擎的核心组件:
        • 2.2.1 解释器(Interpreter)
          • 逐条解释字节码指令并执行。
          • 适合启动阶段或执行频率较低的代码。
        • 2.2.2 即时编译器(JIT Compiler)
          • 将热点代码编译为本地机器码。
          • 常见的 JIT 编译器:
            • C1 编译器(Client Compiler) :优化较少,编译速度快,适合客户端应用。
            • C2 编译器(Server Compiler) :优化较多,编译速度慢,适合服务器端应用。
          • Graal 编译器 :一种新的 JIT 编译器,支持更多的优化和动态编译。
        • 2.2.3 垃圾回收器(Garbage Collector)
          • 负责管理堆内存,回收不再使用的对象。
          • 与执行引擎紧密协作,确保程序运行时的内存效率。
        • 2.2.4 本地方法接口(JNI,Java Native Interface)
          • 允许 Java 代码调用本地(C/C++)代码。
          • 执行引擎通过 JNI 调用本地方法。
    • 三、字节码与执行引擎的交互
      • 3.1 类加载
        • 类加载器将 .class 文件加载到 JVM 中,并生成对应的 Class 对象。
        • 字节码被存储在方法区(Method Area)中。
      • 3.2 字节码解释与编译
        • 执行引擎从方法区读取字节码。
        • 如果是解释执行,逐条解释字节码并执行。
        • 如果是即时编译,将热点代码编译为本地机器码并缓存。
      • 3.3 方法调用
        • 执行引擎通过方法表找到方法的字节码。
        • 如果是本地方法,通过 JNI 调用本地代码。
      • 3.4 内存管理
        • 执行引擎与垃圾回收器协作,管理堆内存和栈内存。
        • 栈内存用于存储方法的局部变量和操作数栈。
    • 四、字节码与执行引擎的优化
      • 4.1 方法内联(Method Inlining)
        • 将小方法的字节码直接嵌入到调用者方法中,减少方法调用的开销。
      • 4.2 逃逸分析(Escape Analysis)
        • 分析对象的生命周期,如果对象不会逃逸出方法,则可以在栈上分配内存,减少垃圾回收的压力。
      • 4.3 循环优化
        • 对循环结构进行优化,例如循环展开、循环剥离等。
      • 4.4 锁消除(Lock Elision)
        • 如果锁对象不会被其他线程访问,则消除锁操作,提高性能。
    • 五、工具与调试
      • javap
        • Java 自带的字节码反编译工具,可以查看 .class 文件的字节码。示例: javap -c MyClass
      • JITWatch
        • 一个可视化工具,用于分析 JIT 编译器的行为。
      • JVM 参数
        • 通过 JVM 参数控制执行引擎的行为,例如:
          • -Xint :强制使用解释模式。
          • -Xcomp :强制使用编译模式。
          • -XX:+PrintCompilation :打印 JIT 编译日志。
  • 性能监控
    • 1. JVM 性能监控的核心指标
      • 1.1 内存使用情况
        • 堆内存(Heap Memory)
          • 新生代(Young Generation):包括 Eden 区和 Survivor 区。老年代(Old Generation):存放长期存活的对象。
        • 非堆内存(Non-Heap Memory)
          • 方法区(Metaspace):存储类元数据。
          • 直接内存(Direct Memory):NIO 使用的堆外内存。
      • 1.2 垃圾回收(GC)
        • GC 频率 :垃圾回收发生的次数。
        • GC 时间 :每次垃圾回收的耗时。
        • GC 类型 :Minor GC(年轻代 GC)、Major GC(老年代 GC)、Full GC(全局 GC)。
      • 1.3 线程状态
        • 线程数 :当前 JVM 中的线程总数。
        • 线程状态 :运行中(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)等。
      • 1.4 CPU 使用率
        • JVM 进程的 CPU 使用率。
        • 高 CPU 使用率可能由死循环、频繁 GC 或线程竞争引起。
      • 1.5 类加载
        • 已加载类的数量。
        • 类加载器的行为。
      • 1.6 JIT 编译
        • JIT 编译的方法数量。
        • 编译时间和优化效果。
    • 2. JVM 性能监控工具
      • 2.1 命令行工具
        • jps
          • 列出当前系统中所有的 Java 进程。示例: jps -l
        • jstat
          • 监控 JVM 的内存和 GC 情况。
          • 示例:
            • jstat -gc <pid> :显示 GC 统计信息。 jstat -gcutil <pid> :显示 GC 利用率。
        • jmap
          • 生成堆内存快照(Heap Dump)。
          • 示例:
            • jmap -heap <pid> :显示堆内存使用情况。 jmap -dump:format=b,file=heap.hprof <pid> :生成堆内存快照。
        • jstack
          • 生成线程快照(Thread Dump)。示例: jstack <pid> > thread.txt
        • jinfo
          • 查看和修改 JVM 参数。示例: jinfo -flags <pid>
      • 2.2 图形化工具
        • JConsole
          • Java 自带的图形化监控工具。
          • 可以监控内存、线程、类加载、MBean 等。
        • VisualVM
          • 功能强大的图形化监控工具。
          • 支持插件扩展,可以分析堆内存、线程、CPU 等。
        • Java Mission Control (JMC)
          • Oracle 提供的专业性能监控工具。
          • 支持实时监控、飞行记录(Flight Recorder)和分析。
        • MAT (Eclipse Memory Analyzer)
          • 用于分析堆内存快照的工具。
          • 可以检测内存泄漏和优化内存使用。
      • 2.3 APM 工具
        • Prometheus + Grafana
          • Prometheus 用于采集 JVM 指标,Grafana 用于可视化。
          • 需要配合 JMX Exporter 或 Micrometer 使用。
        • SkyWalking
          • 分布式系统的 APM 工具,支持 JVM 监控。
        • Arthas
          • 阿里巴巴开源的 Java 诊断工具。
          • 支持实时监控、方法调用追踪、热修复等。
    • 3. JVM 性能监控方法
      • 3.1 内存监控
        • 使用 jstat 监控堆内存使用情况。
        • 使用 jmap 生成堆内存快照,并用 MAT 分析。
        • 监控 Metaspace 的使用情况,避免类元数据溢出。
      • 3.2 GC 监控
        • 使用 jstat -gc 监控 GC 频率和时间。
        • 使用 -Xlog:gc* 参数打印详细的 GC 日志。
        • 使用 VisualVMJMC 分析 GC 行为。
      • 3.3 线程监控
        • 使用 jstack 生成线程快照,分析线程状态。
        • 使用 VisualVMJMC 实时监控线程。
      • 3.4 CPU 监控
        • 使用 tophtop 查看 JVM 进程的 CPU 使用率。
        • 使用 jstack 分析高 CPU 使用率的线程。
      • 3.5 类加载监控
        • 使用 jstat -class 监控类加载情况。
        • 使用 JConsoleVisualVM 查看类加载器行为。
      • 3.6 JIT 监控
        • 使用 -XX:+PrintCompilation 打印 JIT 编译日志。
        • 使用 JMC 分析 JIT 编译行为。
  • JVM 源码与高级特性
  • 实践与工具使用

更多推荐