JVM内存模型与垃圾回收

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

  • 工作流程
    1. 初始标记(STW):标记GC Roots直接关联对象
    2. 并发标记:从GC Roots开始遍历对象图
    3. 重新标记(STW):修正并发标记期间变动
    4. 并发清除:清理死亡对象
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 常见调优策略

  1. 减少Full GC频率:合理设置老年代大小
  2. 优化新生代配置:调整Eden和Survivor比例
  3. 选择合适的垃圾收集器:根据应用特点选择
  4. 避免内存泄漏:及时释放不需要的对象引用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值