在 Java 中堆Heap是 JVM 管理的最大、最核心的一块内存区域。几乎所有的对象实例和数组都在这里诞生、存活、消亡。一、核心概念什么是堆空间1. 通俗定义堆空间是 JVM 运行时的 **“通用大仓库”**。你写的代码new出来的所有对象、数组都被扔到这个仓库里。这里没有门大家所有线程都能进来拿东西。2. 专业定义堆Heap是 JVM 运行时数据区域所有类实例和数组的内存都从这里分配。它是线程共享的区域也是垃圾回收器GC管理的主要区域。二、为什么要划分堆核心逻辑试想一下如果一个大仓库里既有 “临时存放的快递包裹”又有 “要存放一辈子的古董”。清理的时候清洁工怎么分辨扫临时包裹需要翻遍整个仓库。扫古董又不敢乱动。结果清理效率极低且容易产生碎片仓库里出现很多坑。Java 的解决思路分代收集Generational Collection根据对象的生命周期把堆逻辑上划分为不同区域。核心事实98% 的对象都是 “朝生夕死”创建后很快就没用了。只有少数对象会长久存活。基于这个事实JVM 把堆分成了新生代和老年代分别采用不同的回收算法实现效率最大化。三、堆空间的详细划分JDK 8 标准结构目前主流的 JVMHotSpot内存结构如下[ 堆内存 (Heap) ] | |---- [ 新生代 (Young Generation) ] -- 占堆的 1/3 | | | |---- [ Eden 区 ] -- 占新生代的 80% | | | |---- [ Survivor 0区 (From) ] -- 占新生代的 10% | | | |---- [ Survivor 1区 (To) ] -- 占新生代的 10% | |---- [ 老年代 (Old Generation) ] -- 占堆的 2/3 | |---- [ 元空间 (Metaspace) ] -- JDK8 属于本地内存方法区实现1. 新生代Young Generation“临时仓库”存放刚创建、生命周期短的对象。Eden伊甸园对象的出生地。几乎所有新创建的对象都在这里分配因为分配速度快。当 Eden 满了就会触发Minor GC。Survivor幸存区有两个S0 和 S1永远一个在用一个为空。存放经过 Minor GC 依然存活的对象。核心作用过滤掉短命对象避免它们直接进入老年代。2. 老年代Old Generation“永久仓库”存放生命周期长、经过多次回收依然存活的对象。来源新生代对象熬过了 N 次 GC默认 15 次。大对象直接进入老年代。Survivor 区放不下的存活对象。GC 类型触发Major GC或Full GC速度比 Minor GC 慢很多。3. 元空间Metaspace“类档案仓库”注意它属于方法区的实现不属于堆在本地内存。存放类信息、常量、静态变量等。JDK8 以后用它替代了永久代。四、对象分配与流转流程保姆级图解理解堆核心就是理解对象的一生。第一步分配诞生新对象→ 优先分配到Eden 区。如果 Eden 区空间不足 → 触发Minor GC。第二步Minor GC清理新生代扫描 Eden 区和From 区当前在用的幸存区。标记存活对象。将存活对象复制到To 区空的幸存区。清空 Eden 区和 From 区。交换角色To 区变成 From 区From 区变成 To 区。对象年龄 1。第三步晋升进入老年代如果对象年龄达到阈值默认 15或者 Survivor 区装不下了 → 进入老年代。第四步Full GC整堆清理老年代满了 → 触发Major GC。Major GC 后还是满了 → 触发Full GC清理整堆 元空间。五、为什么要设计两个 Survivor 区经典面试题你可能会问为什么非要两个 Survivor 区S0/S1能不能只留一个1. 如果只有一个区域会发生什么Eden 满了 → GC → 存活对象复制到 Survivor。再次 GC → Survivor 满了存活对象怎么办没有空间存放 → 只能直接把存活对象扔进老年代。后果老年代迅速填满OOM 风险剧增。2. 双 Survivor 的作用空间利用率Eden 1 个 Survivor 90% 的新生代空间。只浪费 10% 的空间比复制算法的 50% 浪费好得多。避免碎片复制算法保证了内存连续。年龄计数可以记录对象存活了多少次 GC。一句话总结两个 Survivor 区是为了保证新生代内存连续、空间利用率最高90%。六、常用 JVM 参数实战配置作为开发者你直接通过参数控制堆空间划分。参数含义建议-Xms初始堆大小建议与-Xmx设为相同避免运行时动态扩展内存。-Xmx最大堆大小服务器内存的 1/2 ~ 3/4。-Xmn新生代大小关键参数。增大新生代可以减少对象晋升老年代的频率但也会增加单次 GC 时间。建议设为堆的 1/2。-XX:SurvivorRatioEden 与 Survivor 比例默认是8(Eden:S8:1)。-XX:MaxTenuringThreshold晋升老年代年龄默认15。如果对象多可适当调小如果想少进老年代可调大。-XX:MetaspaceSize元空间初始阈值JDK8 专用。通用推荐配置JDK8# 堆内存固定为 4G -Xms4g -Xmx4g # 新生代固定为 2G (堆的一半) -Xmn2g # 使用 G1 收集器最通用 -XX:UseG1GC # 开启 OOM 自动生成堆快照 -XX:HeapDumpOnOutOfMemoryError七、常见 OOM 与堆空间的关系堆空间是 OOMOutOfMemoryError的重灾区看懂报错就能快速定位。Java heap space原因堆内存不够用了。可能内存泄漏静态集合没清理、大对象过多、堆参数设置太小。GC overhead limit exceeded原因GC 干了 98% 的活只收回 2% 的内存陷入死循环。本质内存泄漏 堆太小。Metaspace / PermGen space原因类加载太多JDK8 是元空间JDK7 是永久代。解决增大-XX:MaxMetaspaceSize。