1. JVM内存模型概述
1.1 JVM内存区域划分
JVM内存模型是Java程序运行的基础,理解其结构对于性能调优至关重要。
1.1.1 程序计数器(Program Counter Register)
- 作用:记录当前线程执行的字节码指令地址
- 特点:线程私有,不会发生OutOfMemoryError
- 实现:在HotSpot中通过寄存器实现
1.1.2 Java虚拟机栈(Java Virtual Machine Stack)
- 结构:由栈帧组成,每个方法调用创建一个栈帧
- 栈帧内容:
- 局部变量表:存储方法参数和局部变量
- 操作数栈:字节码指令的操作数临时存储区域
- 动态链接:指向运行时常量池的方法引用
- 方法返回地址:方法正常退出或异常退出的返回地址
- 异常情况:
- StackOverflowError:栈深度超过虚拟机允许深度
- OutOfMemoryError:栈扩展时无法申请到足够内存
1.1.3 本地方法栈(Native Method Stack)
- 用途:为Native方法服务
- 实现:HotSpot将本地方法栈和虚拟机栈合二为一
1.1.4 Java堆(Java Heap)
- 地位:JVM管理的最大内存区域,所有对象实例分配的主要区域
- 分代结构:
复制代码
Java堆 ├── 新生代(Young Generation) │ ├── Eden区 │ ├── Survivor From区(S0) │ └── Survivor To区(S1) └── 老年代(Old Generation/Tenured) - 分配策略:
- 对象优先在Eden区分配
- 大对象直接进入老年代
- 长期存活对象进入老年代
1.1.5 方法区(Method Area)/元空间(Metaspace)
- 存储内容:
- 类信息:类的版本、字段、方法、接口等描述信息
- 运行时常量池:编译期生成的字面量和符号引用
- 即时编译器编译后的代码缓存
- JDK变化:
- JDK7:字符串常量池移至Java堆
- JDK8:永久代被元空间取代,使用本地内存
2. 垃圾回收机制
2.1 垃圾回收算法
2.1.1 标记-清除算法(Mark-Sweep)
java复制代码
// 伪代码示例 void markSweepGC() { // 标记阶段:标记所有可达对象 markReachableObjects(); // 清除阶段:回收未标记对象 sweepUnmarkedObjects(); }
- 优点:实现简单,不需要移动对象
- 缺点:产生内存碎片,效率不高
2.1.2 标记-复制算法(Mark-Copy)
java复制代码
// 新生代垃圾回收示例 void copyingGC() { // 将Eden和From区存活对象复制到To区 copyLiveObjects(eden, survivorFrom, survivorTo); // 清空Eden和From区 clear(eden); clear(survivorFrom); // 交换From和To区 swap(survivorFrom, survivorTo); }
- 适用场景:新生代回收,存活对象少
- 优点:无内存碎片,实现简单
- 缺点:需要额外内存空间
2.1.3 标记-整理算法(Mark-Compact)
java复制代码
void markCompactGC() { // 标记存活对象 markLiveObjects(); // 整理内存,消除碎片 compactMemory(); }
- 适用场景:老年代回收
- 优点:无内存碎片,内存利用率高
- 缺点:移动对象成本高
2.2 分代收集理论
2.2.1 分代假说
- 弱分代假说:绝大多数对象都是朝生夕灭
- 强分代假说:熬过越多次垃圾收集的对象越难消亡
- 跨代引用假说:跨代引用相对于同代引用仅占极少数
2.2.2 记忆集与卡表
java复制代码
// 卡表实现示例 class CardTable { private byte[] cardTable; private static final int CARD_SIZE = 512; // 每张卡片对应512字节 // 标记卡片为脏卡片 public void markCardDirty(long address) { int cardIndex = (int)(address >>> 9); // 除以512 cardTable[cardIndex] = DIRTY_CARD; } }
2.3 垃圾收集器详解
2.3.1 Serial收集器
bash复制代码
# JVM参数配置 -XX:+UseSerialGC
- 特点:单线程,简单高效
- 适用场景:客户端应用,小内存环境
2.3.2 Parallel收集器
bash复制代码
# 并行收集器配置 -XX:+UseParallelGC -XX:ParallelGCThreads=4 -XX:MaxGCPauseMillis=200
- 特点:多线程并行,吞吐量优先
- 调优参数:
MaxGCPauseMillis:最大停顿时间GCTimeRatio:吞吐量大小
2.3.3 CMS收集器
bash复制代码
# CMS收集器配置 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSCompactAtFullCollection
- 工作流程:
- 初始标记(STW):标记GC Roots直接关联对象
- 并发标记:从GC Roots开始遍历对象图
- 重新标记(STW):修正并发标记期间变动
- 并发清除:清理死亡对象
2.3.4 G1收集器
bash复制代码
# G1收集器配置 -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:G1HeapRegionSize=16m
- 设计理念:将堆分割为多个大小相等的Region
- 回收策略:优先回收垃圾最多的Region
- 关键特性:
- 可预测停顿时间
- 无内存碎片
- 并发与并行
2.3.5 ZGC收集器
bash复制代码
# ZGC配置 -XX:+UseZGC -XX:+UnlockExperimentalVMOptions
- 目标:停顿时间不超过10ms
- 技术特点:
- 并发收集
- 基于Region
- 使用染色指针技术
3. 内存分配策略
3.1 对象分配规则
java复制代码
// 大对象直接进入老年代示例 public class LargeObjectAllocation { public static void main(String[] args) { // 大数组直接分配到老年代 byte[] largeArray = new byte[4 * 1024 * 1024]; // 4MB } }
3.2 TLAB(Thread Local Allocation Buffer)
java复制代码
// TLAB相关JVM参数 -XX:+UseTLAB -XX:TLABSize=256k -XX:TLABRefillWasteFraction=64
3.3 逃逸分析与栈上分配
java复制代码
public class EscapeAnalysis { // 对象不逃逸,可能栈上分配 public void noEscape() { Object obj = new Object(); // obj只在方法内使用 } // 对象逃逸,必须堆分配 public Object escape() { Object obj = new Object(); return obj; // 对象逃逸到方法外 } }
4. 性能监控与调优
4.1 GC日志分析
bash复制代码
# GC日志配置 -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime
4.2 内存调优参数
bash复制代码
# 堆内存配置 -Xms2g -Xmx4g -XX:NewRatio=3 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 # 元空间配置 -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
4.3 常见调优策略
- 减少Full GC频率:合理设置老年代大小
- 优化新生代配置:调整Eden和Survivor比例
- 选择合适的垃圾收集器:根据应用特点选择
- 避免内存泄漏:及时释放不需要的对象引用

611

被折叠的 条评论
为什么被折叠?



