JVM--堆
JVM–堆
背景
-
JVM–堆
-
博主以黑马JVM进行学习
定义
- 通过
new关键字创建的对象实例和数组,都会占用堆内存(补充:对象的元数据存在元空间,而非堆) - 堆是 JVM 运行时数据区中最大的一块内存区域,也是 GC(垃圾回收)的核心区域。
特点
- 线程共享:整个 JVM 进程中只有一个堆,所有线程都能访问堆中的对象,因此堆中对象的读写需要考虑线程安全(比如用
synchronized、Atomic等); - 有垃圾回收机制:堆是 GC 唯一的核心工作区域(虚拟机栈 / 本地方法栈无需 GC,程序计数器无 GC 必要),堆内存会被划分为不同区域(年轻代、老年代、元空间),不同区域采用不同的 GC 策略;
- 可动态扩展:堆内存大小可通过 JVM 参数调整(默认会根据物理内存自动调整),也可设置固定大小。
堆的内存溢出
- 堆内存溢出错误:OutOfMemoryError:Java heap space(最常见的 OOM 类型)
- 堆溢出原因:
- 内存泄漏:对象不再使用但 GC 无法回收(比如静态集合长期持有对象引用、未关闭的资源连接);
- 内存溢出:正常业务创建的对象过多 / 过大,超出堆内存上限(比如一次性加载百万级数据到内存);
- 核心参数:
-Xms:堆内存初始大小(如-Xms200m,建议和-Xmx设为相同值,避免运行时动态扩容消耗性能);-Xmx:堆内存最大上限(如-Xmx512m,超出该值则抛出堆 OOM);
堆内存诊断
-
jps工具:查看当前系统中有哪些java进程(定位目标进程 ID)
-
jps # 输出示例:12345 YourMainClass(12345是进程ID)1
2
3
4
5
6
7
* jmap工具:查看堆内存占用情况 jmap - heap 进程id
* ```bash
jmap -heap 进程ID # 查看堆的整体配置和使用情况(年轻代、老年代占用率)
jmap -histo 进程ID # 查看堆中对象的数量、大小(按内存占用排序,定位大对象)
jmap -dump:format=b,file=heapdump.hprof 进程ID # 导出堆快照文件(用于后续分析)
-
-
jconsole工具:图形界面的,多功能的监测工具,可以连续监测(JDK 自带,无需额外安装)
- 启动方式:命令行输入
jconsole,选择目标 Java 进程即可连接; - 核心功能:实时监测堆内存变化、GC 次数 / 耗时、线程状态等,支持连续监测。
- 启动方式:命令行输入
-
案例:垃圾回收后,内存占用仍然很高
-
场景:程序运行一段时间后,GC 频繁触发,但回收后堆内存占用仍居高不下(大概率是内存泄漏)
-
使用jvisualvm工具(JDK 自带,可视化分析神器)
- 可视化的虚拟机
- 堆Dump功能:导出堆快照文件(.hprof);
-
总结
- 堆是 JVM 最大的线程共享内存区域,
new创建的对象实例 / 数组都存于堆中,需考虑线程安全; - 堆是 GC 的核心区域,堆内存溢出会抛出
OutOfMemoryError: Java heap space,可通过-Xms/-Xmx调整堆大小,核心解决思路是排查内存泄漏 / 减少大对象创建; - 堆内存诊断常用工具:jps(查进程)→ jmap(查静态内存)→ jconsole(实时监测)→ jvisualvm(堆 Dump 分析),其中 jvisualvm 是排查内存泄漏的核心工具。
补充:堆的分代模型
-
堆内存内部会划分为:
-
年轻代(Young Generation):存储新创建的对象,GC 频率高(Minor GC),分为 Eden 区、S0/S1(Survivor)区;
-
老年代(Old Generation):存储存活时间长的对象,GC 频率低(Major GC/Full GC);
-
元空间(Metaspace):JDK 8 后取代永久代,存储类的元数据(不属于堆,但常和堆一起讨论);
这是 GC 策略的基础,理解分代模型能更好地解释 “为何 GC 后内存仍高”(比如老年代对象无法被 Minor GC 回收)。
-