1.Java 虚拟机(JVM)
·
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)
- 1. JVM 是什么?
- 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。
- 新生代(Young Generation) :
- 异常 :
- 如果堆内存不足,会抛出
OutOfMemoryError。
- 如果堆内存不足,会抛出
- 作用 :
- 3、栈(Stack)
- Java 虚拟机栈(Java Virtual Machine Stack) :
-
栈结构
-
作用 :
- 每个线程 私有的 内存区域,用于 存储 方法调用的栈帧 (Stack Frame)。
- 栈帧包括:局部变量表 、操作数栈、动态链接、方法返回地址。
-
栈结构
-
(1)局部变量表 :
- 主要存储方法参数和局部变量。
- 编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)
- 编译期可知的各种数据类型(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。
- 作用 :
- Java 虚拟机栈(Java Virtual Machine Stack) :
- 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); // 分配直接内存
- 1、方法区(Method Area)
- 三、内存区域的线程共享性
- 线程共享的区域 :
- 方法区
- 堆
- 线程私有的区域 :
- 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:设置每个线程的栈大小。
- 参数:
- 1、方法区(元空间)
- 一、JVM 运行时内存区域的划分
- 字节码与执行引擎
- 一、 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.1.1 解释执行 :
- 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 调用本地方法。
- 2.2.1 解释器(Interpreter) :
- 三、字节码与执行引擎的交互
- 3.1 类加载 :
- 类加载器将 .class 文件加载到 JVM 中,并生成对应的 Class 对象。
- 字节码被存储在方法区(Method Area)中。
- 3.2 字节码解释与编译 :
- 执行引擎从方法区读取字节码。
- 如果是解释执行,逐条解释字节码并执行。
- 如果是即时编译,将热点代码编译为本地机器码并缓存。
- 3.3 方法调用 :
- 执行引擎通过方法表找到方法的字节码。
- 如果是本地方法,通过 JNI 调用本地代码。
- 3.4 内存管理 :
- 执行引擎与垃圾回收器协作,管理堆内存和栈内存。
- 栈内存用于存储方法的局部变量和操作数栈。
- 3.1 类加载 :
- 四、字节码与执行引擎的优化
- 4.1 方法内联(Method Inlining) :
- 将小方法的字节码直接嵌入到调用者方法中,减少方法调用的开销。
- 4.2 逃逸分析(Escape Analysis) :
- 分析对象的生命周期,如果对象不会逃逸出方法,则可以在栈上分配内存,减少垃圾回收的压力。
- 4.3 循环优化 :
- 对循环结构进行优化,例如循环展开、循环剥离等。
- 4.4 锁消除(Lock Elision) :
- 如果锁对象不会被其他线程访问,则消除锁操作,提高性能。
- 4.1 方法内联(Method Inlining) :
- 五、工具与调试
- javap :
- Java 自带的字节码反编译工具,可以查看 .class 文件的字节码。示例:
javap -c MyClass。
- Java 自带的字节码反编译工具,可以查看 .class 文件的字节码。示例:
- JITWatch :
- 一个可视化工具,用于分析 JIT 编译器的行为。
- JVM 参数 :
- 通过 JVM 参数控制执行引擎的行为,例如:
-Xint:强制使用解释模式。-Xcomp:强制使用编译模式。-XX:+PrintCompilation:打印 JIT 编译日志。
- 通过 JVM 参数控制执行引擎的行为,例如:
- javap :
- 一、 JVM 字节码
- 性能监控
- 1. JVM 性能监控的核心指标
- 1.1 内存使用情况
- 堆内存(Heap Memory) :
- 新生代(Young Generation):包括 Eden 区和 Survivor 区。老年代(Old Generation):存放长期存活的对象。
- 非堆内存(Non-Heap Memory) :
- 方法区(Metaspace):存储类元数据。
- 直接内存(Direct Memory):NIO 使用的堆外内存。
- 堆内存(Heap Memory) :
- 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 编译的方法数量。
- 编译时间和优化效果。
- 1.1 内存使用情况
- 2. JVM 性能监控工具
- 2.1 命令行工具
- jps :
- 列出当前系统中所有的 Java 进程。示例:
jps -l。
- 列出当前系统中所有的 Java 进程。示例:
- 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。
- 生成线程快照(Thread Dump)。示例:
- jinfo :
- 查看和修改 JVM 参数。示例:
jinfo -flags <pid>。
- 查看和修改 JVM 参数。示例:
- jps :
- 2.2 图形化工具
- JConsole :
- Java 自带的图形化监控工具。
- 可以监控内存、线程、类加载、MBean 等。
- VisualVM :
- 功能强大的图形化监控工具。
- 支持插件扩展,可以分析堆内存、线程、CPU 等。
- Java Mission Control (JMC) :
- Oracle 提供的专业性能监控工具。
- 支持实时监控、飞行记录(Flight Recorder)和分析。
- MAT (Eclipse Memory Analyzer) :
- 用于分析堆内存快照的工具。
- 可以检测内存泄漏和优化内存使用。
- JConsole :
- 2.3 APM 工具
- Prometheus + Grafana :
- Prometheus 用于采集 JVM 指标,Grafana 用于可视化。
- 需要配合 JMX Exporter 或 Micrometer 使用。
- SkyWalking :
- 分布式系统的 APM 工具,支持 JVM 监控。
- Arthas :
- 阿里巴巴开源的 Java 诊断工具。
- 支持实时监控、方法调用追踪、热修复等。
- Prometheus + Grafana :
- 2.1 命令行工具
- 3. JVM 性能监控方法
- 3.1 内存监控
- 使用
jstat监控堆内存使用情况。 - 使用
jmap生成堆内存快照,并用 MAT 分析。 - 监控 Metaspace 的使用情况,避免类元数据溢出。
- 使用
- 3.2 GC 监控
- 使用
jstat -gc监控 GC 频率和时间。 - 使用
-Xlog:gc*参数打印详细的 GC 日志。 - 使用 VisualVM 或 JMC 分析 GC 行为。
- 使用
- 3.3 线程监控
- 使用
jstack生成线程快照,分析线程状态。 - 使用 VisualVM 或 JMC 实时监控线程。
- 使用
- 3.4 CPU 监控
- 使用
top或htop查看 JVM 进程的 CPU 使用率。 - 使用
jstack分析高 CPU 使用率的线程。
- 使用
- 3.5 类加载监控
- 使用
jstat -class监控类加载情况。 - 使用 JConsole 或 VisualVM 查看类加载器行为。
- 使用
- 3.6 JIT 监控
- 使用
-XX:+PrintCompilation打印 JIT 编译日志。 - 使用 JMC 分析 JIT 编译行为。
- 使用
- 3.1 内存监控
- 1. JVM 性能监控的核心指标
- JVM 源码与高级特性
- 实践与工具使用
更多推荐





所有评论(0)