第451集JVM从基础到架构实战
|字数总计:5.2k|阅读时长:21分钟|阅读量:
JVM从基础到架构实战
1. 概述
1.1 JVM的重要性
Java虚拟机(JVM)是Java平台的核心,负责执行Java字节码,管理内存,进行垃圾回收等。深入理解JVM对于Java开发人员至关重要,可以帮助我们编写高性能代码,进行性能调优,解决生产环境问题。
JVM的价值:
- 跨平台:一次编译,到处运行
- 内存管理:自动内存管理和垃圾回收
- 性能优化:通过JVM调优提升应用性能
- 问题诊断:通过JVM工具诊断和解决生产问题
1.2 JVM发展历程
| Java版本 |
JVM版本 |
主要特性 |
| Java 1.0 |
HotSpot 1.0 |
初始版本 |
| Java 1.2 |
HotSpot 1.2 |
性能优化 |
| Java 1.4 |
HotSpot 1.4 |
性能大幅提升 |
| Java 1.5 |
HotSpot 1.5 |
并发性能优化 |
| Java 1.6 |
HotSpot 1.6 |
G1 GC预览 |
| Java 1.7 |
HotSpot 1.7 |
G1 GC正式版 |
| Java 1.8 |
HotSpot 1.8 |
移除永久代,Metaspace |
| Java 9+ |
HotSpot 9+ |
模块化、ZGC、Shenandoah GC |
1.3 主流JVM实现
| JVM实现 |
开发组织 |
特点 |
| HotSpot |
Oracle/Sun |
最广泛使用,性能优秀 |
| OpenJ9 |
IBM/Eclipse |
低内存占用,启动快 |
| GraalVM |
Oracle |
高性能,多语言支持 |
| Zing |
Azul |
低延迟GC,商业产品 |
1.4 本文内容结构
本文将从以下几个方面全面解析JVM:
- JVM基础:JVM架构、内存模型
- 类加载机制:类加载过程、双亲委派模型
- 内存管理:堆内存、方法区、栈内存
- 垃圾回收:GC算法、GC收集器
- 字节码执行:字节码结构、执行引擎
- 性能调优:JVM参数调优、GC调优
- 故障排查:内存泄漏、GC问题诊断
- 架构实战:生产环境JVM配置
2. JVM基础
2.1 JVM架构
2.1.1 JVM整体架构
JVM架构图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| ┌─────────────────────────────────────────┐ │ Java应用程序 │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ Class文件 │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 类加载子系统 │ │ (Class Loader Subsystem) │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 运行时数据区 │ │ (Runtime Data Areas) │ │ ┌──────────┐ ┌──────────┐ │ │ │ 方法区 │ │ 堆 │ │ │ └──────────┘ └──────────┘ │ │ ┌──────────┐ ┌──────────┐ │ │ │ PC寄存器│ │ Java栈 │ │ │ └──────────┘ └──────────┘ │ │ ┌──────────┐ │ │ │ 本地方法栈│ │ │ └──────────┘ │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 执行引擎 │ │ (Execution Engine) │ │ ┌──────────┐ ┌──────────┐ │ │ │ 解释器 │ │ JIT编译器│ │ │ └──────────┘ └──────────┘ │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 本地方法接口 │ │ (Native Method Interface) │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 本地方法库 │ │ (Native Method Libraries) │ └─────────────────────────────────────────┘
|
2.1.2 JVM核心组件
1. 类加载子系统(Class Loader Subsystem):
- 负责加载Class文件
- 将字节码转换为JVM内部数据结构
2. 运行时数据区(Runtime Data Areas):
- 方法区(Method Area)
- 堆(Heap)
- 程序计数器(PC Register)
- Java虚拟机栈(Java Virtual Machine Stack)
- 本地方法栈(Native Method Stack)
3. 执行引擎(Execution Engine):
- 解释器(Interpreter)
- JIT编译器(Just-In-Time Compiler)
- 垃圾回收器(Garbage Collector)
4. 本地方法接口(Native Method Interface):
2.2 JVM内存模型
2.2.1 运行时数据区详解
1. 程序计数器(PC Register):
1 2 3 4
| - 线程私有 - 记录当前线程执行的字节码指令地址 - 唯一不会发生OutOfMemoryError的区域
|
2. Java虚拟机栈(Java Virtual Machine Stack):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ┌─────────────┐ │ 栈帧1 │ ← 当前方法 ├─────────────┤ │ 栈帧2 │ ├─────────────┤ │ 栈帧3 │ └─────────────┘
┌─────────────────┐ │ 局部变量表 │ ├─────────────────┤ │ 操作数栈 │ ├─────────────────┤ │ 动态链接 │ ├─────────────────┤ │ 方法返回地址 │ └─────────────────┘
|
栈的特点:
- 线程私有
- 每个方法对应一个栈帧
- 可能出现StackOverflowError和OutOfMemoryError
3. 本地方法栈(Native Method Stack):
- 为本地方法(Native Method)服务
- 与Java虚拟机栈类似
4. 堆(Heap):
1 2 3 4 5 6 7 8 9 10 11 12
| ┌─────────────────────────────┐ │ 堆 (Heap) │ ├─────────────────────────────┤ │ 新生代 (Young Generation) │ │ ┌──────────┬──────────┐ │ │ │ Eden │ Survivor │ │ │ │ │ S0/S1 │ │ │ └──────────┴──────────┘ │ ├─────────────────────────────┤ │ 老年代 (Old Generation) │ └─────────────────────────────┘
|
堆的特点:
- 线程共享
- 存储对象实例
- 垃圾回收的主要区域
- 可能出现OutOfMemoryError
5. 方法区(Method Area):
Java 8之前:
- 永久代(PermGen)
- 存储类信息、常量、静态变量
**Java 8+**:
- 元空间(Metaspace)
- 使用本地内存,不再受JVM堆内存限制
方法区存储内容:
- 类信息(Class信息)
- 运行时常量池
- 静态变量
- JIT编译后的代码
2.2.2 内存参数配置
堆内存参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| -Xms2g
-Xmx4g
-Xmn1g
-XX:NewRatio=2
-XX:SurvivorRatio=8
|
方法区参数(Java 8之前):
1 2 3 4 5
| -XX:PermSize=256m
-XX:MaxPermSize=512m
|
元空间参数(Java 8+):
1 2 3 4 5
| -XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
|
栈内存参数:
1 2 3 4
| -Xss1m
-XX:ThreadStackSize=1024
|
3. 类加载机制
3.1 类加载过程
3.1.1 类加载的5个阶段
1. 加载(Loading):
- 通过类的全限定名获取类的二进制字节流
- 将字节流转换为方法区的运行时数据结构
- 在内存中生成Class对象
2. 验证(Verification):
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
3. 准备(Preparation):
4. 解析(Resolution):
5. 初始化(Initialization):
- 执行类构造器
<clinit>()方法
- 初始化类变量和静态代码块
3.1.2 类加载示例
1 2 3 4 5 6 7 8 9 10 11 12
| public class ClassLoadingExample { static { System.out.println("静态代码块执行"); } private static int value = 10; public static void main(String[] args) { System.out.println("main方法执行"); System.out.println("value = " + value); } }
|
执行顺序:
- 加载Class文件
- 验证字节码
- 准备:value = 0(默认值)
- 解析符号引用
- 初始化:执行静态代码块,value = 10
3.2 类加载器
3.2.1 类加载器层次结构
双亲委派模型(Parent Delegation Model):
1 2 3 4 5 6 7
| Bootstrap ClassLoader (启动类加载器) ↓ Extension ClassLoader (扩展类加载器) ↓ Application ClassLoader (应用程序类加载器) ↓ Custom ClassLoader (自定义类加载器)
|
类加载器说明:
| 类加载器 |
加载路径 |
说明 |
| Bootstrap |
$JAVA_HOME/jre/lib |
C++实现,JVM一部分 |
| Extension |
$JAVA_HOME/jre/lib/ext |
Java实现 |
| Application |
$CLASSPATH |
系统类加载器 |
| Custom |
自定义 |
用户自定义 |
3.2.2 双亲委派模型
工作原理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } }
|
双亲委派的优势:
- 避免类重复加载
- 保证核心类安全(防止核心类被替换)
- 保证类加载的唯一性
3.2.3 自定义类加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class CustomClassLoader extends ClassLoader { private String classPath; public CustomClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] classData = loadClassData(name); if (classData == null) { throw new ClassNotFoundException(); } return defineClass(name, classData, 0, classData.length); } private byte[] loadClassData(String className) { String fileName = classPath + className.replace('.', '/') + ".class"; try (FileInputStream fis = new FileInputStream(fileName); ByteArrayOutputStream baos = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { baos.write(buffer, 0, len); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); return null; } } }
|
4. 内存管理
4.1 堆内存管理
4.1.1 堆内存结构
Java 8之前的堆结构:
1 2 3 4 5 6 7 8 9 10 11 12 13
| ┌─────────────────────────────┐ │ 堆 (Heap) │ ├─────────────────────────────┤ │ 新生代 (Young) │ │ ┌──────────┬──────────┐ │ │ │ Eden │ Survivor │ │ │ │ │ S0/S1 │ │ │ └──────────┴──────────┘ │ ├─────────────────────────────┤ │ 老年代 (Old) │ ├─────────────────────────────┤ │ 永久代 (PermGen) │ └─────────────────────────────┘
|
Java 8+的堆结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ┌─────────────────────────────┐ │ 堆 (Heap) │ ├─────────────────────────────┤ │ 新生代 (Young) │ │ ┌──────────┬──────────┐ │ │ │ Eden │ Survivor │ │ │ │ │ S0/S1 │ │ │ └──────────┴──────────┘ │ ├─────────────────────────────┤ │ 老年代 (Old) │ └─────────────────────────────┘
┌─────────────────────────────┐ │ 元空间 (Metaspace) │ │ (本地内存) │ └─────────────────────────────┘
|
4.1.2 对象分配过程
对象分配流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 新对象创建 ↓ Eden区是否有空间? ↓ 是 分配到Eden区 ↓ Eden区满 → Minor GC ↓ 存活对象 → Survivor区 ↓ 年龄达到阈值(默认15)? ↓ 是 晋升到老年代 ↓ 否 继续在Survivor区 ↓ 老年代满 → Full GC
|
对象分配代码示例:
1 2 3 4 5 6 7 8 9
| public class ObjectAllocation { public static void main(String[] args) { byte[] obj1 = new byte[1024 * 1024]; byte[] obj2 = new byte[10 * 1024 * 1024]; } }
|
4.2 栈内存管理
4.2.1 栈帧结构
栈帧详细结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ┌─────────────────────────────┐ │ 栈帧 (Stack Frame) │ ├─────────────────────────────┤ │ 局部变量表 (Local Variables)│ │ ┌────┬────┬────┬────┐ │ │ │slot│slot│slot│slot│ │ │ └────┴────┴────┴────┘ │ ├─────────────────────────────┤ │ 操作数栈 (Operand Stack) │ │ ┌────┐ │ │ │ │ │ │ └────┘ │ ├─────────────────────────────┤ │ 动态链接 (Dynamic Linking) │ ├─────────────────────────────┤ │ 方法返回地址 (Return Addr) │ └─────────────────────────────┘
|
局部变量表示例:
1 2 3 4
| public void method(int a, int b) { int c = a + b; String str = "hello"; }
|
局部变量表slot分配:
- slot 0: this(实例方法)
- slot 1: 参数a
- slot 2: 参数b
- slot 3: 局部变量c
- slot 4: 局部变量str(引用)
5. 垃圾回收
5.1 垃圾回收基础
5.1.1 什么是垃圾
垃圾的定义:
- 不再被引用的对象
- 循环引用的对象(如果没有外部引用)
判断对象是否为垃圾的方法:
1. 引用计数法:
- 每个对象有一个引用计数器
- 引用+1,引用失效-1
- 计数器为0的对象是垃圾
- 缺点:无法解决循环引用问题
2. 可达性分析(Java采用):
- 从GC Roots开始向下搜索
- 能够到达的对象是存活对象
- 不能到达的对象是垃圾
GC Roots包括:
- 虚拟机栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象
- 同步锁持有的对象
5.1.2 引用类型
Java 4种引用类型:
1. 强引用(Strong Reference):
1
| Object obj = new Object();
|
2. 软引用(Soft Reference):
1 2 3 4
| import java.lang.ref.SoftReference;
SoftReference<Object> softRef = new SoftReference<>(new Object()); Object obj = softRef.get();
|
3. 弱引用(Weak Reference):
1 2 3 4
| import java.lang.ref.WeakReference;
WeakReference<Object> weakRef = new WeakReference<>(new Object()); Object obj = weakRef.get();
|
- 下次GC时回收
- 适合做缓存(WeakHashMap)
4. 虚引用(Phantom Reference):
1 2 3 4 5
| import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue;
ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
|
- 无法通过虚引用获取对象
- 对象回收时收到通知
- 用于跟踪对象回收
5.2 垃圾回收算法
5.2.1 标记-清除算法(Mark-Sweep)
算法过程:
- 标记:标记所有需要回收的对象
- 清除:回收被标记的对象
缺点:
5.2.2 复制算法(Copying)
算法过程:
- 将内存分为两块
- 只使用其中一块
- 当这一块用完,将存活对象复制到另一块
- 清空当前块
优点:
缺点:
应用:新生代(Eden和Survivor)
5.2.3 标记-整理算法(Mark-Compact)
算法过程:
- 标记:标记所有需要回收的对象
- 整理:将存活对象向一端移动
- 清理:清理边界外的内存
优点:
缺点:
应用:老年代
5.2.4 分代收集算法(Generational Collection)
算法思想:
- 根据对象存活周期将堆分为新生代和老年代
- 新生代使用复制算法
- 老年代使用标记-清除或标记-整理算法
分代收集流程:
1 2 3 4 5 6 7 8 9
| 新对象 → Eden区 ↓ Eden满 → Minor GC ↓ 存活对象 → Survivor区 ↓ 年龄达到阈值 → 老年代 ↓ 老年代满 → Full GC
|
5.3 垃圾收集器
5.3.1 串行收集器(Serial)
特点:
使用参数:
5.3.2 并行收集器(Parallel)
特点:
- 多线程收集
- 适合吞吐量优先的应用
- 服务器模式默认收集器(Java 8之前)
使用参数:
1 2 3 4 5
| -XX:+UseParallelGC
-XX:ParallelGCThreads=4
|
5.3.3 并发标记清除收集器(CMS)
特点:
- 并发收集,减少停顿时间
- 适合响应时间敏感的应用
- Java 9标记为废弃,Java 14移除
使用参数:
1 2 3 4 5
| -XX:+UseConcMarkSweepGC
-XX:ConcGCThreads=2
|
5.3.4 G1收集器(Garbage First)
特点:
- 低延迟,适合大堆内存
- 将堆分为多个Region
- Java 9默认收集器
使用参数:
1 2 3 4 5 6 7 8
| -XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
|
G1堆结构:
1 2 3 4 5 6 7 8 9 10
| ┌─────────────────────────────┐ │ G1堆 (多个Region) │ ├─────────────────────────────┤ │ ┌────┐┌────┐┌────┐┌────┐ │ │ │Eden││Surv││ Old││Hum │ │ │ └────┘└────┘└────┘└────┘ │ │ ┌────┐┌────┐┌────┐┌────┐ │ │ │Eden││Surv││ Old││Hum │ │ │ └────┘└────┘└────┘└────┘ │ └─────────────────────────────┘
|
5.3.5 ZGC收集器
特点:
- 超低延迟(<10ms)
- 适合超大堆内存(TB级别)
- Java 11引入(实验性),Java 15正式版
使用参数:
5.3.6 Shenandoah收集器
特点:
使用参数:
5.4 GC日志分析
5.4.1 GC日志配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| -XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/path/to/gc.log
-Xlog:gc*:file=/path/to/gc.log:time,tags,level
|
5.4.2 GC日志格式
Parallel GC日志:
1
| [GC (Allocation Failure) [PSYoungGen: 8192K->1024K(9216K)] 8192K->2048K(10240K), 0.0123456 secs]
|
G1 GC日志:
1 2 3 4 5 6 7 8 9 10 11 12
| [GC pause (G1 Evacuation Pause) (young), 0.0123456 secs] [Parallel Time: 10.0 ms, GC Workers: 4] [GC Worker Start (ms): Min: 100.0, Avg: 100.1, Max: 100.2] [Ext Root Scanning (ms): Min: 1.0, Avg: 1.1, Max: 1.2] [Update RS (ms): Min: 0.0, Avg: 0.1, Max: 0.2] [Scan RS (ms): Min: 0.0, Avg: 0.1, Max: 0.2] [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0] [Object Copy (ms): Min: 8.0, Avg: 8.1, Max: 8.2] [Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.2] [GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.2] [GC Worker Total (ms): Min: 9.0, Avg: 9.1, Max: 9.2] [GC Worker End (ms): Min: 109.0, Avg: 109.2, Max: 109.4]
|
6. 字节码执行
6.1 字节码基础
6.1.1 Class文件结构
Class文件格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ┌─────────────────┐ │ Magic Number │ (0xCAFEBABE) ├─────────────────┤ │ Minor Version │ ├─────────────────┤ │ Major Version │ ├─────────────────┤ │ Constant Pool │ (常量池) ├─────────────────┤ │ Access Flags │ (访问标志) ├─────────────────┤ │ This Class │ (当前类) ├─────────────────┤ │ Super Class │ (父类) ├─────────────────┤ │ Interfaces │ (接口) ├─────────────────┤ │ Fields │ (字段) ├─────────────────┤ │ Methods │ (方法) ├─────────────────┤ │ Attributes │ (属性) └─────────────────┘
|
6.1.2 查看字节码
使用javap命令:
1 2 3 4 5
| javap -c MyClass.class
javap -verbose MyClass.class
|
示例代码:
1 2 3 4 5
| public class BytecodeExample { public int add(int a, int b) { return a + b; } }
|
字节码输出:
1 2 3 4 5 6
| public int add(int, int); Code: 0: iload_1 // 加载局部变量1(参数a) 1: iload_2 // 加载局部变量2(参数b) 2: iadd // 执行加法 3: ireturn // 返回结果
|
6.2 执行引擎
6.2.1 解释执行
解释器(Interpreter):
6.2.2 编译执行
JIT编译器(Just-In-Time Compiler):
热点代码检测:
6.2.3 混合模式
JVM默认模式:
- 解释器 + JIT编译器
- 启动时使用解释器
- 热点代码使用JIT编译
JIT编译器类型:
C1编译器(Client Compiler):
C2编译器(Server Compiler):
分层编译(Tiered Compilation):
7. 性能调优
7.1 JVM参数调优
7.1.1 堆内存调优
基础参数:
1 2 3 4 5 6 7 8 9 10 11
| -Xms4g
-Xmx4g
-Xmn2g
-XX:NewRatio=2
|
调优建议:
- Xms和Xmx设置为相同值,避免动态调整
- 堆内存 = 物理内存的50%-70%
- 新生代 = 堆内存的1/3-1/4
7.1.2 GC调优
G1 GC调优:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| -XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
-XX:ConcGCThreads=4
-XX:ParallelGCThreads=8
|
Parallel GC调优:
1 2 3 4 5 6 7 8
| -XX:+UseParallelGC
-XX:ParallelGCThreads=8
-XX:+UseAdaptiveSizePolicy
|
7.1.3 方法区调优
Java 8+(Metaspace):
1 2 3 4 5
| -XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
|
7.2 GC调优实战
7.2.1 调优目标
吞吐量优先:
延迟优先:
7.2.2 调优步骤
1. 收集GC日志:
1 2 3
| -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log
|
2. 分析GC日志:
3. 调整参数:
7.3 性能监控工具
7.3.1 jstat
1 2 3 4 5
| jstat -gcutil <pid> 1000 10
jstat -gc <pid> 1000 10
|
7.3.2 jmap
1 2 3 4 5
| jmap -heap <pid>
jmap -dump:format=b,file=heap.hprof <pid>
|
7.3.3 jstack
1 2 3 4 5
| jstack <pid> > thread.txt
jstack <pid> | grep -i deadlock
|
7.3.4 VisualVM
功能:
- 实时监控JVM
- 分析堆转储
- 分析线程转储
- 性能分析
8. 故障排查
8.1 内存泄漏
8.1.1 内存泄漏诊断
症状:
诊断步骤:
1 2 3 4 5 6 7 8 9
| jmap -heap <pid>
jmap -dump:format=b,file=heap.hprof <pid>
|
8.1.2 常见内存泄漏原因
1. 集合类泄漏:
1 2 3 4 5 6 7 8
| public class MemoryLeak { private static List<Object> list = new ArrayList<>(); public void add(Object obj) { list.add(obj); } }
|
2. 监听器泄漏:
1 2 3 4 5 6 7 8
| public class ListenerLeak { public void register() { someObject.addListener(new Listener() { }); } }
|
3. 内部类持有外部类引用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Outer { private Object data; public void method() { new Thread(new Runnable() { @Override public void run() { System.out.println(data); } }).start(); } }
|
8.2 GC问题诊断
8.2.1 Full GC频繁
原因:
解决方案:
1 2 3 4 5 6 7 8
| -Xmx8g
-Xmn4g
-XX:PretenureSizeThreshold=2m
|
8.2.2 GC停顿时间长
原因:
解决方案:
1 2 3 4 5 6
| -XX:+UseG1GC -XX:MaxGCPauseMillis=200
-XX:+UseZGC
|
8.3 CPU占用高
8.3.1 诊断步骤
1 2 3 4 5 6 7 8 9
| top -p <pid>
jstack <pid> > thread.txt
|
8.3.2 常见原因
1. 死循环:
2. 频繁GC:
1 2
| jstat -gcutil <pid> 1000 10
|
3. 锁竞争:
1 2
| jstack <pid> | grep -i "locked"
|
9. 架构实战
9.1 生产环境JVM配置
9.1.1 推荐配置模板
Web应用(G1 GC):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| -Xms4g -Xmx4g
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=16m
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/gc.log
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/heap_dump.hprof
|
批处理应用(Parallel GC):
1 2 3 4 5 6 7 8 9 10 11
| -Xms8g -Xmx8g
-XX:+UseParallelGC -XX:ParallelGCThreads=8
-XX:+PrintGCDetails -Xloggc:/var/log/gc.log
|
9.1.2 不同场景配置
高并发Web应用:
1 2 3
| -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:InitiatingHeapOccupancyPercent=45
|
大数据处理:
1 2 3
| -XX:+UseG1GC -XX:MaxGCPauseMillis=500 -Xmx32g
|
低延迟应用:
9.2 JVM监控方案
9.2.1 监控指标
关键指标:
- 堆内存使用率
- GC频率和耗时
- 线程数量
- CPU使用率
9.2.2 监控工具
1. Prometheus + JMX Exporter:
1 2 3
| java -javaagent:jmx_prometheus_javaagent.jar=1234:config.yml \ -jar application.jar
|
2. JVM监控脚本:
1 2 3 4 5 6 7 8 9 10 11 12
| #!/bin/bash PID=$1
echo "=== JVM Monitor ===" echo "Heap Memory:" jmap -heap $PID | grep -A 10 "Heap Usage"
echo "GC Statistics:" jstat -gcutil $PID 1000 5
echo "Threads:" jstack $PID | grep "java.lang.Thread.State" | sort | uniq -c
|
10. 总结
10.1 核心要点
- JVM架构:类加载、运行时数据区、执行引擎
- 内存管理:堆、栈、方法区的管理
- 垃圾回收:GC算法、GC收集器、GC调优
- 性能调优:JVM参数调优、GC调优
- 故障排查:内存泄漏、GC问题诊断
10.2 架构师建议
JVM选择:
- 生产环境:HotSpot(稳定)
- 低延迟:ZGC或Shenandoah
- 大堆内存:G1 GC
参数调优:
- 根据应用特点选择GC
- 合理设置堆内存大小
- 监控GC日志,持续优化
监控告警:
- 监控堆内存使用率
- 监控GC频率和耗时
- 设置合理的告警阈值
10.3 最佳实践
- 标准化:统一JVM配置标准
- 监控化:实时监控JVM状态
- 文档化:记录调优过程和结果
- 持续优化:根据监控数据持续优化
相关文章: