
JVM 运行时五大内存分区分类、存储内容与对应异常内存分区两大分类标准线程私有 / 线程共享JVM 运行时数据区分为两类核心区别是否每个线程独立拥有内存、是否容易产生 OOM线程私有单线程独占线程销毁内存直接释放OOM 概率低程序计数器 PC Register虚拟机栈 JVM Stack本地方法栈 Native Method Stack线程共享整个进程共用进程关闭才释放内存高频出现 OOM堆 HeapGC 主要回收区域方法区 Method AreaJDK8 后为元空间 MetaspaceOutOfMemoryError内存溢出JVM 运行程序时需要分配一块内存存放对象、类、缓冲区等但没有足够空闲内存满足本次分配需求虚拟机直接抛出 OOM 异常严重时服务进程崩溃。和 StackOverflowError 区分StackOverflowError栈深度超限递归死循环、方法嵌套太多栈空间本身是够的只是层数放不下。OOM整块内存容量耗尽分配新内存失败。五种常见 OOM 类型Java heap space堆内存溢出。new 的对象、数组太多GC 回收后依然没有空闲空间。 场景内存泄漏、无限创建对象、超大集合缓存数据。Metaspace元空间溢出。大量动态生成类CGLIB 代理、频繁反射、热加载、动态脚本。Direct buffer memory堆外直接内存溢出。NIO 读写文件 / 网络DirectByteBuffer 堆外内存没手动释放。虚拟机栈 OOM 栈支持动态扩容操作系统无剩余内存分配新栈帧极少出现。本地方法栈 OOM native 方法申请本地内存耗尽线上几乎遇不到。OOM 根本两大诱因内存参数设置过小-Xmx 堆最大值、元空间上限配置太低代码内存泄漏无用对象被强引用持续持有GC 永远无法回收内存慢慢占满。私有三区计数器、虚拟机栈、本地方法栈随线程销毁共享两区堆、元空间全局共用易 OOM堆存对象、元空间存类信息栈只存引用不存实体对象。五大内存区域逐点详解内存区域线程归属存储内容内存回收方式对应异常调优参数程序计数器线程私有当前线程执行的字节码指令地址线程销毁自动释放无 OOM唯一不会溢出无虚拟机栈线程私有栈帧局部变量表、操作数栈、动态链接、方法出口存放基本类型、对象引用地址方法执行完栈帧自动出栈无 GC 参与StackOverflowError栈深度超限/ 栈 OOM扩容失败-Xss单个栈大小本地方法栈线程私有native 底层 C/C 方法栈帧方法执行完自动释放StackOverflowError / 栈 OOM-Xss堆 Heap线程共享所有 new 创建的对象实例、数组实体细分为新生代Eden、S0、S1、老年代GC 垃圾回收自动清理无用对象java.lang.OutOfMemoryError: Java heap space-Xms初始堆/-Xmx最大堆方法区元空间线程共享类字节码、静态变量、运行时常量池、类 / 方法 / 字段描述信息Full GC 时回收废弃类、常量java.lang.OutOfMemoryError: Metaspace-XX:MaxMetaspaceSize程序计数器PC 寄存器归属线程私有核心作用记录当前线程执行到的字节码行号多线程切换时依靠该记录恢复执行位置存储内容字节码指令地址异常特性JVM 规范唯一不会发生 OOM 的内存区域虚拟机栈Java 栈归属线程私有存储载体栈帧每调用一个方法就创建一个栈帧方法执行完毕栈帧出栈销毁栈帧内部组成局部变量表、操作数栈、动态链接、方法出口存储数据方法内局部基本数据类型、对象引用地址抛出两类异常StackOverflowError递归死循环、多层方法嵌套栈深度超出最大限制OutOfMemoryError虚拟机栈支持动态扩容系统剩余内存不足无法分配新栈帧本地方法栈归属线程私有作用专门执行 native 本地方法底层 C/C 系统调用、IO 操作等结构、异常类型和虚拟机栈完全一致同样会栈溢出、内存溢出堆 Heap重点归属线程共享整个 JVM 最大内存区域GC 核心回收区存储内容所有 new 关键字创建的对象实例、数组实体堆内存细分新生代Eden 区、Survivor0、Survivor1、老年代 Old 区溢出异常java.lang.OutOfMemoryError: Java heap space触发场景对象过多、大对象、内存泄漏堆内存分配不足方法区JDK8 元空间 Metaspace归属线程共享版本变化JDK1.7 及之前永久代 PermGenJDK1.8 彻底移除永久代使用堆外本地内存元空间存储内容类字节码文件、静态变量、运行时常量池、方法 / 字段描述、接口信息溢出异常java.lang.OutOfMemoryError: Metaspace触发场景大量动态代理 CGLIB、频繁反射、动态生成类补充知识点堆外直接内存 Direct Buffer高频 OOM 不属于五大运行时内存区但线上极易溢出来源NIO 的 DirectByteBuffer读写文件、网络 IO 使用堆外内存限制不受 - Xmx 堆内存参数管控溢出异常java.lang.OutOfMemoryError: Direct buffer memory虚拟机栈与堆内存核心对比表对比维度虚拟机栈堆内存线程归属线程私有线程共享存储数据局部变量、基本类型、对象引用地址new 创建的对象、数组实体内存回收机制方法执行完自动释放无 GC 参与依靠 GC 垃圾回收清理无用对象溢出类型StackOverflowError、栈 OOMJava heap space 堆内存溢出内存参数控制-Xss 控制单个栈大小-Xms/-Xmx 控制堆初始 / 最大内存生命周期跟随线程线程销毁栈直接释放跟随进程GC 持续回收闲置对象判断对象存活的两种算法引用计数、可达性分析 GC Roots判断算法实现逻辑优点致命缺点JVM 是否使用引用计数对象自带计数器引用 ±10 则回收逻辑简单实时回收循环引用无法回收内存泄漏不使用可达性分析 GC Roots从根对象遍历引用链不可达为垃圾解决循环引用回收准确需要扫描全堆产生 STW 停顿HotSpot 默认全行业通用引用计数算法已淘汰仅理论了解核心原理给每一个 Java 对象单独维护一个整型引用计数器当有变量引用该对象计数器1引用失效变量销毁、重新赋值计数器-1计数器数值为 0判定为垃圾对象可被 GC 回收。致命缺陷无法商用循环引用问题两个对象 A、BA 内部持有 B 的引用B 内部持有 A 的引用外部无任何变量指向 A 和 B。 此时两者计数器都不为 0GC 永远识别不了这是垃圾无法回收长期堆积造成内存泄漏、堆 OOM。结论HotSpot 虚拟机完全放弃该算法。可达性分析算法HotSpot 虚拟机默认生产主流核心逻辑定义一系列GC Roots 根对象作为起点从根向下遍历整个引用链遍历过程中能够访问到的对象 存活对象从 GC Roots 出发无法遍历到达的对象 垃圾对象等待 GC 回收完美解决循环引用的痛点。五类标准 GC Roots虚拟机栈中局部变量引用的对象方法内创建的对象方法区中静态变量、常量引用的对象static 全局集合、常量对象本地方法栈 native 方法引用的底层对象同步锁 synchronized 持有的 monitor 锁对象Java 系统内置对象主线程、类加载器、系统 Class 等。可达性分析完整执行流程第一步找到全部 GC Roots收集虚拟机栈、静态变量、native 方法、锁、系统线程等根对象。第二步遍历引用链以 GC Roots 为起点顺着对象引用一路遍历沿途接触到的全部标记存活。第三步区分存活 / 垃圾能走到 存活完全走不到、无任何根可达 垃圾对象。第四步回收垃圾根据 GC 算法复制 / 标记清除 / 标记整理清理垃圾释放堆内存。核心优势即使两个对象互相循环引用只要 GC Roots 碰不到照样判定垃圾回收解决引用计数致命问题。对象四大引用类型强引用Object o new Object()只要存在强引用GC 永远不会回收日常代码默认使用静态集合极易靠强引用造成内存泄漏。软引用 SoftReference内存充足不回收堆空间即将溢出时才回收适合做缓存。弱引用 WeakReference只要发生 GC无论内存是否充足都会被回收缓存、临时映射场景使用。虚引用 PhantomReference无法通过引用获取对象仅用于监控对象被回收的通知极少业务使用。引用类型定义说明GC 回收时机常见使用场景JDK 对应类举例说明强引用普通new对象赋值给变量默认就是强引用只要强引用存在GC永远不会回收即使 OOM 也不回收日常业务代码默认使用绝大部分对象都是强引用无特殊类默认即强引用Object obj new Object()软引用 SoftReference用 SoftReference 包装对象内存充足时保留内存充足时不回收堆内存即将 OOM 溢出时才回收内存敏感型缓存图片缓存、大对象缓存java.lang.ref.SoftReference浏览器图片缓存内存够就留着不够就清掉弱引用 WeakReference用 WeakReference 包装对象生命周期比软引用更短只要发生 GC无论内存是否充足都会被回收临时映射、ThreadLocal、缓存、避免内存泄漏java.lang.ref.WeakReferenceThreadLocal 的 key 就是弱引用防止 ThreadLocal 内存泄漏虚引用 PhantomReference最弱的引用完全不影响对象生命周期无法通过引用获取对象对象被回收时收到通知仅用于监控对象回收堆外内存回收跟踪、NIO DirectByteBuffer 回收监控java.lang.ref.PhantomReference监控堆外直接内存释放避免 DirectBuffer 泄漏强引用默认就是永不回收OOM 也不丢 软引用内存不够才回收适合做大缓存弱引用遇到 GC 就回收适合临时映射 虚引用拿不到对象只用来监控回收通知。四大基础 GC 垃圾回收算法原理、优缺点、适用区域标记 - 清除算法 Mark-Sweep执行流程标记阶段从 GC Roots 遍历堆把所有存活对象打上标记清除阶段扫描整个堆直接释放所有未标记的垃圾对象内存。优点实现逻辑简单不需要移动任何存活对象。缺点回收完成后产生大量不连续内存碎片分配大对象时找不到连续空间频繁触发 Full GC需要两次完整遍历堆内存GC 效率偏低。适用场景仅老年代简易回收场景生产环境不会单独使用。复制算法 Copying执行流程将内存划分为两块同等大小区域 A、B平时只使用 A 区存放新对象GC 触发时把 A 区内所有存活对象复制到空白 B 区保证内存连续复制完成后直接清空 A 区交换 A、B 角色下一次 GC 复用。优点复制后内存完全连续无内存碎片分配对象速度快仅移动指针即可分配。缺点永久浪费一半堆内存空间存活对象数量多时复制成本极高停顿时间变长。适用场景新生代EdenSurvivor新生代大部分对象朝生夕死存活对象极少复制开销很低。标记 - 整理算法 Mark-Compact执行流程标记阶段同标记清除标记全部存活对象整理阶段将所有存活对象向内存一端压缩、移动全部紧凑排列边界以外整块空间全部清空形成连续空闲内存。优点回收后无内存碎片空闲内存连续适合存放大对象。缺点需要大量移动存活对象STW 停顿时间很长性能损耗大。适用场景老年代老年代存活对象多复制算法浪费一半内存不适合。分代收集算法 Generational Collection商用主流组合算法核心思想根据对象存活生命周期把堆分为新生代、老年代不同分代匹配最优回收算法新生代对象生命周期短、存活少 →复制算法老年代对象长期存活、数量多 →标记清除 / 标记整理。业务执行流程新创建对象分配在 Eden 区Eden 占满触发 Minor GC存活对象复制到 Survivor多次 GC 仍存活的对象晋升到老年代老年代空间不足触发 Full GC使用标记整理回收。优点结合复制、标记整理两者优势综合性能最优所有商用回收器Parallel GC、CMS、G1、ZGC底层均基于分代收集。缺点需要维护两套回收逻辑虚拟机底层实现复杂。GC 算法核心流程优点缺点适用内存区域标记清除标记存活对象直接清理垃圾实现简单无需移动对象产生大量内存碎片两次全堆遍历效率低老年代简易场景不单独生产使用复制存活对象复制到备用分区清空原分区无内存碎片对象分配速度快永久浪费 50% 内存存活多复制开销大新生代 Eden/Survivor标记整理标记存活全部向一端压缩移动无内存碎片空闲空间连续大量移动对象STW 停顿时间长老年代分代收集新生代复制、老年代标记整理组合使用分代适配综合性能最优多代逻辑管理复杂完整堆内存线上通用标准高并发营销场景完整 JVM 内存 GC 串联实战流程业务场景描述营销大促大量用户同时领取优惠券、下单循环创建优惠券 DTO、订单临时对象。对象分配与 GC 完整流程用户并发请求代码执行 new 创建大量临时对象全部分配到新生代 Eden 区Eden 区快速被占满触发 Minor GC大部分一次性临时请求对象无外部引用直接被回收少量缓存、待支付订单存活对象通过复制算法转移到 Survivor 区存活对象每经历一次 Minor GC年龄 1多次 GC 后仍存活的长期缓存对象优惠券活动缓存、用户基础信息达到年龄阈值晋升到老年代若代码存在全局静态 Map 无限存入用户数据老年代对象持续增长老年代剩余空间不足以容纳新晋升对象触发 Full GC采用标记整理算法压缩回收无效老年代对象若堆内存参数配置过小Full GC 后依然没有连续空闲内存分配对象抛出Java heap spaceOOM服务宕机代码大量使用 CGLIB 动态代理、反射生成类类字节码持续填充元空间最终触发 Metaspace 元空间溢出 OOM。该场景下会出现的两类线上故障故障 1频繁 Minor GC接口轻微卡顿现象接口响应变慢无大量报错GC 日志 Minor GC 频率极高根因循环内不停 new 临时大对象、未复用实体 DTOEden 快速填满优化对象池复用对象、缩小方法外对象生命周期、调大新生代 Xmn 参数故障 2频繁 Full GCCPU 打满、大量接口超时现象CPU 持续 100%大量请求超时日志频繁打印 Full GC根因全局静态集合内存泄漏、大对象直接进入老年代、堆内存配置不足应急方案导出 dump 文件MAT 工具分析内存占用临时调大 - Xmx 堆内存业务低峰期修复内存泄漏代码短期临时对象 → 新生代 Minor GC复制算法长期缓存静态对象 → 老年代 Full GC标记整理动态生成代理类 / 反射 → 元空间溢出 OOM线上 JVM 内存 / GC 故障分级、现象、应急处理方案一级故障频繁 Minor GC接口轻微卡顿现象GC 日志短时间大量打印 Minor GC接口响应延迟小幅上涨无大批量超时、无服务宕机CPU 轻度升高未打满。根因循环逻辑中持续 new 临时对象、DTO 未复用新生代 Eden 内存分配过小单次请求创建大量短生命周期大对象。应急处理临时调大新生代参数-Xmn扩大 Eden 容量临时限流降低并发请求量。长期优化抽取通用 DTO复用对象减少 new 次数局部方法内创建临时对象避免提升生命周期拆分超大集合、分批处理数据。二级故障频繁 Full GCCPU 打满大量接口超时现象日志持续输出 Full GCCPU 占用长期接近 100%前端大批量请求超时服务吞吐量暴跌。根因静态 Map/List 无限存放数据内存泄漏超大对象直接分配到老年代堆内存-Xmx设置过小老年代无连续内存存放晋升对象。应急处理立即开启 dump 快照使用 MAT 工具分析内存占用临时调高-Xmx堆最大内存缓解压力临时下线高耗内存活动接口。长期优化清理全局静态集合采用软 / 弱引用做业务缓存限制单次查询返回数据量拆分大对象调整对象晋升年龄阈值优化分代内存比例。三级故障StackOverflowError 栈溢出服务崩溃现象控制台抛出StackOverflowError进程直接重启崩溃。根因递归逻辑无终止条件无限递归多层方法循环嵌套过深单线程栈内存-Xss配置偏小。应急处理紧急修复递归代码增加递归深度判断终止条件临时调大-Xss单栈内存大小。长期优化递归改循环实现业务递归业务增加最大深度拦截校验。四级故障Metaspace 元空间 OOM现象报错java.lang.OutOfMemoryError: Metaspace服务宕机。根因CGLIB、动态代理、反射频繁生成新类热加载框架重复加载类废弃类无法卸载元空间上限参数过小。应急处理临时调大-XX:MaxMetaspaceSize临时关闭非必要动态代理、热更新功能。长期优化缓存代理类避免循环生成 Class定期清理无用动态类配置类卸载参数。五级故障堆外直接内存 Direct Buffer OOM现象报错Direct buffer memoryIO、文件上传接口异常。根因NIO 使用 DirectByteBuffer 未手动释放堆外内存大文件一次性读取缓冲区无限制堆外内存上限参数过小。应急处理限制单次上传 / 读取文件大小调高-XX:MaxDirectMemorySize。长期优化使用 try-finally 主动释放堆外缓冲区文件流分段读写不一次性加载全部文件。故障等级故障类型核心现象快速应急手段一级频繁 Minor GC接口轻微卡顿、GC 日志 Minor GC 多调大 - Xmn、临时限流二级频繁 Full GCCPU100%、大量请求超时导出 dump、调高 - Xmx、下线高耗内存接口三级栈溢出 StackOverflowError程序直接崩溃重启修复递归逻辑、增大 - Xss四级元空间 OOMMetaspace 溢出宕机调大 MaxMetaspaceSize、关闭多余动态代理五级堆外内存 OOM文件 / IO 接口报错限制文件大小、调高堆外内存上限JVM 生产长期内存与 GC 调优规范堆内存基础参数规范Xms、Xmx、Xmn-Xms堆初始内存-Xmx堆最大堆内存 生产规范两者设置相等原因运行时堆不会动态扩容 / 缩容避免扩容触发额外 GC 停顿性能更稳定。-Xmn新生代总大小Eden 两个 Survivor 经验分配新生代占整个堆内存 1/3 ~ 1/4 高并发接口、大量临时对象场景适度调大 Xmn减少 Minor GC 频率。Eden 与 Survivor 默认比例 8:1:1 Eden 占新生代 80%S0、S1 各 10%保证复制算法备用区充足。元空间 Metaspace 调优规范JDK8 无永久代元空间使用本地堆外内存默认无上限生产必须配置-XX:MaxMetaspaceSize限制最大值防止无限占用操作系统内存频繁动态代理、反射、热更新项目适当调高元空间上限尽量复用 Class、代理类循环内禁止重复生成 CGLIB 动态类。虚拟机栈参数 -Xss-Xss控制单条线程栈内存大小默认一般 1M递归业务、多层方法嵌套可适度调大线程数量极多的服务不能设置过大会直接耗尽操作系统内存。堆外直接内存规范配置-XX:MaxDirectMemorySize限制 NIO 堆外缓冲区上限DirectByteBuffer 使用完毕必须手动释放不要依赖 GC 回收大文件上传、网络 IO 接口做分片处理禁止一次性加载超大缓冲区。对象生命周期编码规范从根源减少 GC 与内存泄漏短期临时对象在方法内创建方法执行结束自动销毁不要存入全局静态集合全局缓存优先使用软引用 SoftReference、弱引用 WeakReference内存不足自动释放禁止使用无限扩容静态 List/Map 存放业务数据用户、订单、优惠券循环中避免频繁 new 大对象抽取对象复用、使用对象池批量查询分页、分片读取杜绝一次性加载百万级数据进内存。GC 日志与故障快照配置线上必开开启完整 GC 日志记录每次 GC 耗时、内存变化、停顿时间配置 OOM 自动导出堆 dump 文件-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/xxx/dump.hprofdump 文件用于离线 MAT 工具分析内存泄漏、大对象占用无 dump 则 OOM 故障无法定位。引用类型使用规范普通业务实体强引用短期局部使用无问题缓存场景优先软引用内存紧张自动清理容器 key、临时映射弱引用避免内存泄漏堆外内存监控场景虚引用跟踪缓冲区回收。递归与大对象规避规范递归增加最大深度判断防止 StackOverflowError复杂递归改用循环实现禁止一次性创建超大数组、超大集合拆分分批处理避免超大对象直接分配到老年代频繁触发 Full GC。调优维度配置 / 编码规范核心目的堆内存 Xms/Xmx数值保持一致避免堆动态扩容产生额外 GC 停顿新生代 - Xmn占堆内存 1/3~1/4高并发调大减少 Eden 快速填满降低 Minor GC 频率元空间 MaxMetaspaceSize设置固定上限防止动态类耗尽本地内存元空间 OOM栈内存 - Xss默认 1M递归场景适度增大避免栈溢出多线程服务不宜过大堆外内存 MaxDirectMemorySize设置合理上限限制 NIO 缓冲区占用防止 Direct buffer OOM代码对象管理方法内创建临时对象静态集合用软 / 弱引用从根源杜绝内存泄漏减少 Full GC故障日志配置开启 GC 日志、OOM 自动 dump故障发生后可回溯定位内存问题