🌱 JVM分代内存模型:新生代 vs 老年代 vs 永久代/元空间 🧠

作为JVM内存管理的核心设计,分代收集理论建立在弱分代假说上:

“绝大多数对象的生命周期都很短,只有少数对象会长期存活”

我们通过内存划分和不同的GC策略,显著提升垃圾回收效率。下面是结构化详解:


一、JVM内存区域的逻辑划分 📦

TypeError: Cannot read properties of undefined (reading 'v')
  1. 🌿 新生代 (Young Generation)

    • 占比:通常占堆内存的 1/3(如-Xmx4G时约为1.3G)
    • 对象特点:存放 新创建的对象(约98%对象在此处夭折)
    • GC机制Minor GC(频率高、速度快、STW短)
    • 子区域
      • Eden (伊甸园)
        • 新对象诞生地(除大对象直接进老年代)
        • GC触发:Eden满时触发Minor GC
      • Survivor (幸存区)
        • S0 + S1:两块等大内存(HotSpot默认各占新生代10%)
        • 存放从Eden中存活一次GC的对象
        • 对象年龄计数器:每次Survivor区存活+1,达到阈值(默认15)晋升老年代
  2. 🌲 老年代 (Old Generation)

    • 占比:堆内存的 2/3
    • 对象特点
      • 从新生代晋升而来的长期存活对象
      • 大对象(通过-XX:PretenureSizeThreshold设定阈值,如2MB)
    • GC机制Major GC / Full GC(速度慢、STW长)
    • 触发条件
      • 老年代空间不足
      • Minor GC后存活对象过多(Survivor放不下)
  3. **📚 永久代 (<=JDK7) / 元空间 (>=JDK8) **

    • 存储内容
      • 类元数据(Class信息)、方法字节码、常量池
    • 关键变化
      • JDK7及之前:PermGen(永久代),属于堆的一部分
      • JDK8+Metaspace(元空间),使用本地内存,不再在堆中
    • 溢出问题
      • 永久代:java.lang.OutOfMemoryError: PermGen space
      • 元空间:java.lang.OutOfMemoryError: Metaspace

二、分代GC协作流程 🔄

示例:一个对象从创建到死亡

  1. Object obj = new Object(); → 在Eden区分配内存
  2. Eden满 → 触发Minor GC
    • 清理死亡对象
    • 存活对象复制到Survivor S0,年龄+1
  3. 下一次Minor GC
    • 清理Eden+S0,存活对象复制到S1
    • 对象年龄再次+1(S0清空)
  4. 当对象年龄≥15-XX:MaxTenuringThreshold)→ 晋升老年代
  5. 老年代满 → 触发Full GC(回收整个堆)

💡 空间分配担保机制
在Minor GC前,JVM检查老年代剩余空间是否>新生代对象总大小。
若不足,则触发Full GC(避免Minor GC后存活对象无处存放)。


三、为什么需要分代? 🚀

场景 分代策略的优化效果
高频死亡对象 Minor GC快速清理(只扫新生代,目标区域小)
低频率回收老对象 避免重复扫描老年代,大幅减少GC时间
内存碎片问题 新生代用“复制算法”(无碎片),老年代用“标记-整理”

四、关键JVM参数 ⚙️

# 堆内存
-Xmx4g             # 最大堆内存(老年代+新生代)
-Xms4g             # 初始堆内存

# 新生代
-XX:NewRatio=2     # 老年代/新生代=2/1(即新生代占堆1/3)
-XX:SurvivorRatio=8 # Eden/Survivor=8/1(如新生代900MB:Eden800MB,S0+S1各50MB)

# 晋升策略
-XX:MaxTenuringThreshold=15 # 晋升年龄阈值
-XX:PretenureSizeThreshold=2M # 大对象直接进老年代

# 元空间(JDK8+)
-XX:MaxMetaspaceSize=256m    # 限制元空间大小

五、内存溢出(OOM)场景 🚨

  1. 新生代OOM:高频创建短命对象(如循环中new对象)
    • 现象:频繁Minor GC,GC后Eden/Survivor仍满
  2. 老年代OOM:内存泄漏(如静态集合持有对象)/ 长周期大对象
    • 现象:Full GC后无法回收空间
  3. 元空间OOM:大量动态生成类(如CGlib代理)

🔍 排查工具

  • jstat -gc <pid> 查看各区域用量
  • jmap -histo:live <pid> 分析对象分布
  • -XX:+HeapDumpOnOutOfMemoryError 生成内存快照

六、生产案例 🌰

场景:Web应用查询返回大量数据(未分页),导致:

  1. 临时对象撑满Eden → 频繁Minor GC
  2. 部分大数组直接进入老年代
  3. 老年代逐渐累积 → 触发Full GC(应用卡顿)
    优化
  • 增加新生代比例(-XX:NewRatio=1,使新生代占堆50%)
  • 优化分页查询,减少单次数据量
  • 添加缓存层,复用对象