程序计数器
- 为每个线程私有
- 指向线程对应程序的当前执行地址,指向虚拟机栈中对应栈帧
- 如果执行方法为Native方法,程序计数器为null
- 没有OutOfMemory和StackOverflow异常
Java虚拟机栈
- 为每个线程私有
- 虚拟机栈的元素为栈帧,每个方法在执行时创建一个栈帧,因此一对一关系
- 栈帧存储当前方法的局部变量表,操作数栈,方法出口等信息
- 有StackOverflow和OutOfMemory异常
本地方法栈
- 为每个线程私有
- 为Native方法服务
- 每个Native方法执行时创建一个本地方法栈帧,来存储局部变量等信息
- 有StackOverflow和OutOfMemory异常
Java堆
- 被所有进程共享
- 存放对象实例(包括成员变量),存放数组,存放静态变量
- 堆为垃圾回收GC管理的主要区域
- 根据GC逻辑,堆分为 新生代 和 老生代,新生代细分 Eden区,Survivor区
- Eden区大多数对象刚出生的地方,Survivor区为经历一次或多次GC仍存活的对象的地方,老生代为更长生命周期的对象
- GC会根据各个区域(新生代区域的Eden和Survivor,老生代区域)的占满情况来进行清理,在Eden或Survivor占满时,会执行MinorGC,清理新生代,然后存活下来的进行晋升,如Eden晋升到Survivor,Survivor晋升到老生代。在老生代空间不足,或元空间空间不足等时,会触发MajorGc或FullGC,对整个java堆全面清理
元空间
- 存储类描述,以及类中的成员变量描述,成员方法描述,运行时常量池
- 创建对象需要元空间对应的类描述
- 有OutOfMemory异常(类太多)
运行时:内存区域的联动过程
当我们执行以下这行代码时,JVM 内部三个区域是如何配合的?
Useruser=newUser();- 元空间 (Metaspace):
- JVM 首先检查元空间中是否已经加载了
User类。 - 如果没有,加载器会把
User的结构信息(如上述表格内容)填入元空间。 - 它是“图纸”:JVM 根据元空间里的字段定义,算出创建一个
User对象需要占多少字节。
- JVM 首先检查元空间中是否已经加载了
- Java 堆 (Heap):
- JVM 根据“图纸”计算的大小,在堆中分配一块内存。
- 它是“实物”:这里存放真正的
name="张三"、age=25等数据。
- 虚拟机栈 (Stack):
- 在当前线程的栈帧中,局部变量表里存入一个变量
user。 - 它是“指针”:
user的值就是堆中那个User实例的物理内存地址。
- 在当前线程的栈帧中,局部变量表里存入一个变量
直接内存
- 操作系统级的本地内存,由操作系统管理,不属于JVM数据区的一部分