注意区别于 Java 内存模型
JVM 内存结构,和 Java 虚拟机的运行时区域有关
Java 内存模型,和 Java 的并发编程有关
结构
JVM 定义了五种运行时数据区,分别是虚拟机栈、本地方法栈、程序计数器、Java 堆、方法区
线程隔离的:虚拟机栈、本地方法栈、程序计数器
线程共享的:堆、方法区
程序计数器
用作当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。在多线程的情况下,多个线程轮流切换执行,所以为了线程切换后能够恢复到正确的执行位置,需要程序计数器来告诉线程接下来该执行哪条指令
虚拟机栈
每个 Java 虚拟机线程都有自己私有的 Java 虚拟机栈,跟线程同时创建,跟线程有相同生命周期
Java 虚拟机栈描述的是 Java 方法执行的内存模型:每一个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法从调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中的入栈到出栈的过程
可抛出的异常:
-
StackOverflowError
线程请求分配的栈容量超过 Java 虚拟机栈允许的最大容量,常见于递归调用
-
OutOfMemoryError
虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时,不太常见
本地方法栈
本地方法栈区别于虚拟机栈,当调用的方法是本地方法(例如 C 语言实现的方法)时,会用到本地方法栈
同样会抛出 StackOverflowError 和 OutOfMemoryError 异常
堆
堆是线程共享的,分为新生代和老年代
新生代又分为 Eden 区和两个 Survivor 区 S0(Survivor From) 和 S1(Survivor To),而且 Survivor 区总有一个是空的
可抛出的异常:
-
OutOfMemoryError
当在堆上申请不到足够的空间
方法区
用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译(JIT)后的代码等数据
Java 7
JDK 1.7 中,习惯将方法区称为“永久代”,因为 HotSpot 虚拟机用永久代来实现方法区
方法区是 JVM 规范中定义的一个区域,但规范只做理论指导,而永久代是 HotSpot 虚拟机在此规范上的具体实现方式
可抛出的异常:
-
OutOfMemoryError: PermGen space
当永久代被占满时
Java 8
到了 JDK 1.8,HotSpot 虚拟机已经完全移除永久代,也就是说不使用永久代的方式来实现方法区,也就是 JDK1.8 中不存在永久代的概念了,取而代之的是 Metaspace(元空间),它也是对方法区的一个实现方式,相比于永久代方式,元空间方式解决了永久代方式存在的一些问题
元空间数据分配在本地内存中,也就是系统可用内存,所以默认情况下,不会发生 OOM 问题
举🌰
public class Test {
/**
* 类的变量
*/
private static int a;
/**
* 类成员变量
*/
private int b;
/**
* 局部变量
*/
public void test(int c){
int d;
}
}
变量 a 是类变量,存放在 方法区,线程共享
变量 b 是成员变量,随着对象一起存放在 堆 内存,线程共享
变量 c 和 d 是局部变量,存放在 栈 内存,线程独享