文章目录
- [一、JVM 内存结构是啥?](#一、JVM 内存结构是啥?)
- 二、程序计数器
- [三、Java 虚拟机栈](#三、Java 虚拟机栈)
- 四、本地方法栈
- 五、堆(内存最大的区域)
- 六、方法区
- 七、常见问题,一次说清
-
- [1. 堆和栈有什么区别?](#1. 堆和栈有什么区别?)
- [2. 方法区到底在哪?](#2. 方法区到底在哪?)
大家好,你有没有想过:我们写的 Java 程序,new 出来的对象去了哪?这些问题的答案,其实都藏在 JVM 的内存结构 里。今天,带你们搞清楚 JVM 是什么划分内存区域的。
一、JVM 内存结构是啥?
我们可以把 JVM 想象成一栋楼,这栋楼专门用来运行 Java 程序。楼里有不同的房间,每个房间干不同的事。这些"房间",就是 JVM 的内存区域。
JVM 把内存划分为 5 个主要部分:
- 程序计数器
- Java 虚拟机栈
- 本地方法栈
- 堆
- 方法区
二、程序计数器
-
它是干啥的?
记录当前线程执行到哪一行字节码了,方便线程之间的切换。
-
特点:
每个线程都有自己的程序计数器(线程私有)。
它是 JVM 中唯一不会发生
OutOfMemoryError
的区域。如果当前执行的是 Java 方法,它记录的是代码行号
三、Java 虚拟机栈
-
它是干啥的?
每调用一个方法,JVM 就会为这个方法分配一块内存空间,叫做"栈帧",然后把这个栈帧压到栈里。方法执行完,就弹出来。它是线程私有的,生命周期和线程一样。
-
栈帧里装了啥?
局部变量表
操作数栈
动态链接
方法返回地址
-
常见异常:
StackOverflowError :递归太深,比如 "main" 调用自己,栈太深了超出存储容量。
OutOfMemoryError:线程开得太多,每个线程都要栈,内存不够用了。
四、本地方法栈
它是干啥的?
和虚拟机栈类似,但它服务的是 native 方法------也就是用 C/C++ 写的底层方法,比如 Thread.start()、Object.hashCode()。它也是线程私有的。
虽然我们平时不直接写 native 方法,但很多 Java 底层功能都依赖它。
五、堆(内存最大的区域)
-
它是干啥的?
所有通过 new 创建的对象,都住在这里。数组也在这里。
-
特点:
所有线程共享,是 JVM 中最大的一块内存。
垃圾回收(GC)主要发生在这里。
可以通过 -Xms (初始大小)和 -Xmx(最大大小)来设置。
堆还分"小区":
- 新生代(Young Generation) :新对象的"临时宿舍"。
- Eden 区:大多数对象出生地。
- Survivor 区(S0、S1):经历过一次 GC 还活着的对象搬到这里。
- 老年代(Old Generation):活了很久的对象,比如缓存、静态集合。
大多数对象"朝生夕死",所以新生代回收频繁但高效;老年代对象少但活得久,回收慢。
六、方法区
- 它是干啥的?
存放类的结构信息,比如:
- 类名、方法名、字段
- 静态变量
- 常量
- 编译后的字节码(运行时常量池)
- 特点:
- 所有线程共享。
- 在 JDK 8 之前叫"永久代"(PermGen),容易溢出。
- JDK 8 开始,改用"元空间"(Metaspace),不再放在堆里,而是使用本地内存(操作系统的内存),所以更稳定。
七、常见问题,一次说清
1. 堆和栈有什么区别?
项目 | 堆 | 栈 |
---|---|---|
存什么 | 对象实例 | 方法调用(栈帧) |
线程共享 | 是 | 否(线程私有) |
回收 | GC 管理 | 方法结束自动弹出 |
速度 | 慢 | 快 |
大小 | 大 | 小 |
2. 方法区到底在哪?
- JDK 7 及以前:在堆内,叫永久代。
- JDK 8+:移到本地内存,叫元空间(Metaspace),默认不限大小(受系统内存限制)。
上述属个人学习后的见解,如有错误欢迎在评论区留言,我们一起进步。