news 2026/10/26 10:23:43

Java 堆内存详解|分代模型、Minor GC / Full GC、OOM 案例演示

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 堆内存详解|分代模型、Minor GC / Full GC、OOM 案例演示

前言

堆是JVM中最重要的一块内存区域,也是垃圾回收的主战场。几乎所有Java对象实例、数组全部分配在堆上。面试经常会问:对象如何分配?什么时候晋升到老年代?Minor GC和Full GC区别?OOM有哪些类型?

本篇从基础概念、JVM启动参数、分代模型、对象流转、各类GC、OOM实战演示完整梳理。

回顾上一篇运行时数据区知识点:堆属于线程共享区域,JVM启动时创建。

一、堆内存基础概念

  1. 一个Java进程对应一个JVM实例,一个JVM只有一块堆内存,所有线程共享堆。
  2. 几乎所有对象、数组都在堆分配。
  3. 堆的大小可以通过JVM启动参数进行调整。
  4. 堆会发生GC,也是OOM内存溢出最常发生的区域。

注意:方法区(永久代/元空间)HotSpot实现称之为Non‑Heap非堆。逻辑上属于堆概念,物理内存上和Java堆相互独立。

1.1 堆核心JVM参数

参数说明默认值
-Xms堆初始内存,等价‑XX:InitialHeapSize物理内存的1/64
-Xmx堆最大内存,等价‑XX:MaxHeapSize物理内存的1/4
-Xmn新生代总大小(Eden+两个Survivor总和)新生代占整个堆1/3,老年代2/3
-XX:MaxTenuringThreshold对象晋升老年代年龄阈值,复制GC的计数,默认1515

生产环境最佳实践:‑Xms和-Xmx设置为相同的值

如果Xms小于Xmx,GC完成后JVM会动态扩容/缩容堆,这个过程会带来性能损耗。生产环境固定大小,避免运行时调整堆容量。

1.2 Java代码查看当前堆内存大小

Runtime类提供API,可以查看程序运行时堆的实际情况。

public class HeapSpaceDemo { public static void main(String[] args) { // totalMemory:当前已经分配给JVM堆的总内存 long totalMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024; // maxMemory:JVM堆能申请到的最大内存,对应-Xmx long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024; // freeMemory:堆中空闲可用内存 long freeMemory = Runtime.getRuntime().freeMemory() /1024 /1024; System.out.println("totalMemory(当前堆): " + totalMemory + " MB"); System.out.println("maxMemory(-Xmx): " + maxMemory + " MB"); System.out.println("freeMemory(空闲): " + freeMemory + " MB"); } }

启动参数示例:‑Xms600m ‑Xmx600m ‑Xmn200m

  • 堆总大小固定600M
  • 新生代总共200M,老年代400M

二、堆逻辑分代模型

分代是逻辑划分,物理内存不一定连续。 HotSpot传统分代堆分为两大块:

  1. Young 新生代:存放生命周期短、朝生夕死的对象;GC频繁。
    • Eden(伊甸园区)
    • Survivor 0(From)
    • Survivor 1(To)

      默认比例 Eden:Survivor0:Survivor1 =8:1:1

  2. Old 老年代:存放存活时间长、多次GC仍然存活的对象;GC频率低。

Survivor永远有一块是空的,叫做To区;另一块叫From区。谁空谁就是To。

2.1 对象分配与完整流转流程

  1. 新创建对象优先分配到Eden区。
  2. Eden空间填满,触发Minor GC(Young GC),发生STW,暂停业务线程。
    • 将Eden中存活对象复制到To Survivor区,对象年龄 + 1。
    • Eden区全部清空;From区存活对象同样复制到To区,年龄+1。
    • 交换From与To身份,原来To变成新的From,原来From清空变为To。
  3. 继续在Eden分配新对象;反复Minor GC,对象在两个Survivor来回拷贝,年龄不断增长。
  4. 对象年龄达到‑XX:MaxTenuringThreshold=15,晋升到老年代。

ASCII示意图模拟一次Minor GC

# GC前 ┌──────────────┬─────────┬─────────┐ │ Eden │ From(S0)│ To(S1) │ │ 大量新对象 │存活对象 │ 空 │ └──────────────┴─────────┴─────────┘ # Minor GC执行,存活对象复制到To ┌──────────────┬─────────┬─────────┐ │ Eden │ From(S0)│ To(S1) │ │ 清空 │ 清空 │存活对象 │ └──────────────┴─────────┴─────────┘ # 交换身份:S1变成From,S0变成To(空) ┌──────────────┬─────────┬─────────┐ │ Eden │ From(S1)│ To(S0) │ │ 分配新对象 │存活对象 │ 空 │ └──────────────┴─────────┴─────────┘

2.2 对象直接进入老年代两种场景

面试高频考点:不是只有年龄到15才会进老年代

  1. 大对象直接分配到老年代对象太大,Eden放不下,Survivor也装不下,直接分配到老年代。

    什么是大对象:很长的数组、大字节数组。大对象尽量避免,会提前耗尽老年代,频繁Full GC。

  2. Survivor空间不足,分配担保机制Minor GC之后存活对象总大小 > To Survivor容量,无法放入Survivor,对象直接晋升到老年代。

总结: 情况1:经历15次Minor GC仍然存活,年龄阈值触发晋升; 情况2:大对象 / Survivor放不下,直接晋升老年代。

2.3 老年代GC流程

老年代对象生命周期长,不会频繁GC。 当老年代内存不足,触发Major GC,Major GC通常会连带触发Full GC。

  • Full GC会回收整个堆(新生代+老年代)+方法区元空间。
  • Full GC STW停顿时间很长,业务系统应当尽量减少Full GC发生。
  • 如果Full GC完成之后,内存仍然不够存放对象 →抛出OOMJava heap space。

三、GC类型详解

3.1 Minor GC(Young GC)

  • 触发条件:Eden区内存耗尽
  • 只回收新生代(Eden + Survivor)
  • 执行频繁,STW时间短,速度快。
  • 会发生STW,暂停全部业务用户线程。

3.2 Major GC(Old GC)

  • 只回收老年代;HotSpot中Major GC一般伴随Full GC一起发生。
  • 速度比Minor GC慢10倍以上,STW停顿时间更长。

3.3 Mixed GC(G1收集器特有)

传统Serial/Parallel/CMS没有Mixed GC,G1专属。 收集全部新生代,同时回收部分老年代Region,目标控制停顿时间。

3.4 Full GC 整堆收集

Full GC会收集:新生代、老年代、元空间(方法区)。STW时间很长,线上尽量避免。

Full GC常见触发场景(面试必背)

  1. 老年代空间不足,对象晋升失败;
  2. 元空间Metaspace内存耗尽;
  3. 手动调用System.gc(),只是建议JVM执行GC,不保证立刻Full GC;
  4. CMS并发模式失败;
  5. G1分配失败。

注意:System.gc()不要在业务代码随便调用,会引发不必要Full GC,严重影响性能。

四、OOM OutOfMemoryError内存溢出

重要理解:OOM不等于堆溢出,OOM是一类错误总称,不同原因会报不同提示。java.lang.OutOfMemoryError,内存溢出:JVM无法再分配足够内存给新对象。

4.1 OOM不同错误类型

错误提示发生位置产生原因
Java heap spaceJava堆Full GC之后,堆内存仍然放不下新对象;内存泄漏 / 创建超大对象
PermGen space永久代(JDK7‑)加载类过多,动态生成大量class;Tomcat部署大量war包
Metaspace元空间(JDK8+)动态代理、CGLIB生成大量类,元空间耗尽
Direct Memory直接内存堆外NIO ByteBuffer分配堆外内存,未释放

4.2 堆OOM代码演示

JVM启动参数设置:‑Xms30m ‑Xmx30m

堆最大只能30M,不断往集合添加字节数组,耗尽堆内存。

import java.util.ArrayList; public class OOMHeapDemo { public static void main(String[] args) throws InterruptedException { ArrayList<byte[]> list = new ArrayList<>(); while(true){ //打印堆状态 long max = Runtime.getRuntime().maxMemory() /1024 /1024; long total = Runtime.getRuntime().totalMemory() /1024 /1024; long free = Runtime.getRuntime().freeMemory() /1024 /1024; System.out.printf("Xmx=%dM total=%dM free=%dM%n",max,total,free); //每次添加1M字节数组 list.add(new byte[1024 * 1024]); Thread.sleep(100); } } }

运行一段时间抛出异常:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

4.3 OOM自动生成堆快照 dump文件

线上发生OOM,程序直接崩溃,来不及现场调试。可以配置JVM参数,OOM发生自动导出堆快照.hprof文件,事后分析内存快照。

# OOM的时候自动dump堆快照 ‑XX:+HeapDumpOnOutOfMemoryError # 指定hprof输出路径,生产写服务器磁盘路径 ‑XX:HeapDumpPath=/data/jvm/dump.hprof

注意:dump文件大小几乎等于堆最大内存,磁盘需要预留足够存储空间。

4.4 hprof快照分析工具

  1. IDEA内置hprof分析工具,直接打开.hprof文件;
  2. Eclipse MAT(Memory Analyzer Tool),专业分析内存泄漏;
  3. JDK自带VisualVM;
  4. YourKit(商业Profiler工具)。

分析重点:

  1. 哪些对象占用内存最大;
  2. 对象引用链,定位是谁持有对象没有释放;
  3. 判断是瞬时大对象,还是长期内存泄漏。

五、面试高频题汇总

  1. 新生代Eden、Survivor默认比例,Survivor from/to交换机制?
  2. 对象什么时候晋升到老年代?两种情况。
  3. Minor GC、Major GC、Full GC分别区别,触发条件?
  4. 为什么生产环境-Xms和-Xmx设置为相同数值?
  5. OOM有哪几种报错,分别对应什么原因?
  6. 什么是分配担保?
  7. 如何配置JVM参数,OOM自动生成堆转储dump文件?
  8. System.gc()作用,能不能保证立刻Full GC?

小贴士

堆分代是理解GC收集器的基础,下一篇我们学习GC理论算法、四种引用、CMS/G1/ZGC垃圾收集器。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 10:22:09

3D 数学基础

GLM 类型别名using Vector2f glm::vec2; using Vector3f glm::vec3; using Vector4f glm::vec4; using Vector2i glm::ivec2; using Vector3i glm::ivec3; using Vector4i glm::ivec4; using Matrix3x3 glm::mat3; using Matrix4x4 glm::mat4; using Color glm::vec3;…

作者头像 李华
网站建设 2026/10/26 10:21:11

采购系统缺陷复盘(二):近 200 条体验缺陷提炼表单设计与审批流的 12 条 UX 铁律【必填可见、暂存语义、待办实时性、乱码定位、批量效率】

✍️ 作者:王文峰(外号:神秘小峯 / 山峯)|中国国学、传统文化与代码爱好者的程序人生,期待你的关注和支持! 采购系统缺陷复盘(二):表单与审批流的 12 条 UX 铁律 一、开篇:体验缺陷为什么值得写一篇 二、铁律 1~7:表单设计七要素 铁律 1:必填可见,前后端双校验 铁…

作者头像 李华
网站建设 2026/10/26 10:17:24

大模型应用场景:RAG vs 微调,小白程序员必收藏!

本文深入探讨了在AI应用场景中&#xff0c;RAG&#xff08;检索增强生成&#xff09;与微调技术的选择问题。文章以电商AI应用岗面试题为例&#xff0c;详细阐述了如何根据业务需求、数据变化、知识更新频率等因素选择合适的技术路径。核心观点包括&#xff1a;RAG适用于需要实…

作者头像 李华
网站建设 2026/10/26 10:16:31

C#入门学习周记2

本周学习了很多&#xff0c;方法&#xff0c;类等等方法方法概念: 一组代码的集合目的: 方便多次调用, 实现代码重用定义c# 里普通函数&#xff08;方法&#xff09;必须写在类&#xff08;或者结构体、记录 record&#xff09;里面&#xff0c;不能单独写在类外面。访问修饰符…

作者头像 李华
网站建设 2026/10/26 10:15:47

海外短剧源码+短剧CMS小程序源码+一站式短剧网站/APP搭建方案

过去两年&#xff0c;短剧从小程序内循环走向 TikTok Minis、独立 H5、海外 APP 多端分发&#xff0c;“短剧源码”这个词的技术含义已经变了。 源码&#xff1a;v.dyedus.top 所以一套合格的“海外短剧 CMS 小程序源码”不能只是把中文后台翻译成英文&#xff0c;而要在数据模…

作者头像 李华