1. JVM运行数据区概览
jvm运行数据区图例
图例说明
- JVM运行数据区可以划分为线程独占和线程共享两部分
- 线程独占(封闭): 每个线程都拥有它的独立的内存空间,jvm在运行程序时就会为线程开辟一个内存空间来存储线程相关的信息,随着线程生命周期创建和销毁
- 线程共享: 所有线程都能够访问当前的数据区域,随着GC的生命周期的创建和销毁
2. JVM运行数据区分解说明
线程共享
-
方法区
- 用于存储被JVM加载的类信息、常量、静 态变量、即时编译器编译(JIT编译)后的代码等数据
- 属于一个逻辑区域,根据不同的hotspot VM来实现,如jdk7方法区存放在永久代,而jdk8存放在元数据空间,通过GC机制来对当前区域进行管理
-
堆内存(也称为“主内存”/“共享内存”区域)
- 划分为老年代, 新生代(Eden/From Survivor/To Survivor)
- JVM启动的时候会根据系统分配的堆内存策略从计算机申请内存空间,主要用于存储类实例信息
- GC回收器本质上是对堆内存的管理
- 此区域会抛出OutOfMemoryError
线程独占
- 虚拟机栈
- 描述虚拟机VM在java在线程中执行方法的内存模型,是属于线程私有的一块独立空间线程栈
- 线程栈包含多个栈帧
- 一个线程会执行一个或者多个方法,一个方法对应一个栈帧
- 栈帧内容包含: 局部变量表,操作数栈,动态链接,方法回收地址,附加信息等,用来存储数据和部分结果过程的数据结构,同时也被用来处理动态链接,方法返回值和异常分派
- 栈帧随着方法的生命周期创建和销毁(不论是方法正常退出还是异常退出)
- 会抛出StackOverflowError
- 本地方法栈
- 描述虚拟机VM执行用native修饰的方法
- 虚拟机VM没有详细描述其规范,不同的VM将由对应不同的厂商实现
- 会抛出StaclOverflowError
- 程序计数器
- 对于执行native方法,程序计数器存储为空
- 对于执行java方法,程序计数器主要是记录当前线程执行的字节码位置,存储字节码执行指令的地址
- 每个线程都会在对应的线程栈创建存储计数器的空间,目的是记录执行字节码指令,因此占用空间比较少
- 作用: 就是在同一时刻,同一个CPU执行只能执行一个线程方法,在并发多线程情况下,为了能够记住当前线程执行的字节码指令位置,需要借助程序计数器来记录对应的地址,当线程被唤醒的时候,将会根据程序计数器的位置继续往下执行
参考jvm规范
https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-2.html#jvms-2.5