JAVA 内存模型
https://www.cnblogs.com/aiqiqi/p/10770864.html
一、JVM运行时区域

- 线程共享的数据:方法区、堆
- 线程隔离的数据:虚拟机栈、本地方法栈、程序计数器
1、程序计数器
- 定义:可看做是当前线程所执行的字节码的行号指示器,字节码解释器工作时通过改变程序计数器的值来选取下一条该执行的字节码指令。
- 作用:① 代码流程控制:字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制;
② 多线程切换:在多线程情况下,程序计数器用来记录当前线程执行的位置,当线程下次被切换回需要执行的时候知道从哪开始。
注:程序计数器是唯一不会出现OOM的内存区域,他的生命周期随线程创建而产生,线程销毁而终止。
2、虚拟机栈

- 说明:java虚拟机栈描述的是java方法执行的内存模型:每个方法执行的同时会创建一个栈帧
- 异常:① StackOverFlowErroy:虚拟机栈无法动态扩展的情况下,线程请求的栈深度大于虚拟机所容许的栈深度;
② OutOfMemoryErroy:虚拟机栈可动态扩展的情况下,当扩展时无法申请到足够的内存;
- 栈帧:栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构。
① 局部变量表:
- 局部变量(方法参数、局部变量)、对象引用(reference类型) 和 returnAddress类型(它指向了一条字节码指令的地址)
- 局部变量表的容量以变量槽为最小单位,每个变量槽都可以存储32位长度的内存空间
② 操作数栈:
③ 动态链接:
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。
在类加载阶段中的解析阶段会将符号引用转为直接引用,这种转化也称为静态解析。
另外的一部分将在每一次运行时期转化为直接引用。这部分称为动态连接。
④ 方法出口:
一个方法开始执行后,只有2种方式可以退出这个方法 :
方法返回指令 : 执行引擎遇到一个方法返回的字节码指令,这时候有可能会有返回值传递给上层的方法调用者,这种退出方式称为正常完成出口。
异常退出 : 在方法执行过程中遇到了异常,并且没有处理这个异常,就会导致方法退出。
无论采用任何退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息。
一般来说,方法正常退出时,调用者的PC计数器的值可以作为返回地址,栈帧中会保存这个计数器值。
而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。
3、本地方法栈
和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务
4、方法区
它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
5、堆
堆是java虚拟机管理的内存中最大的一块,是所有线程共享的内存区域,随着虚拟机启动而创建。
此内存的唯一目的是存放对象实例。
java堆是垃圾收集器管理的主要区域,因此也被称作GC堆,从垃圾回收的角度分为:新生代、老年代、永久代;
① 新生代:大多数对象在新生代中被创建,其中很多对象的生命周期很短;
② 老年代:在新生代中经历了N此垃圾回收后依然存在的对象,会被放入老生代;
③ 永久代:主要存放元数据,例如Class、Method的元信息,与垃圾回收要回收的Java对象关系不大。相对于新生代和年老代来说,该区域的划分对垃圾回收影响比较小。
在 JDK 1.8中移除整个永久代,取而代之的是一个叫元空间(Metaspace)的区域(永久代使用的是JVM的堆内存空间,而元空间使用的是物理内存,直接受到本机的物理内存限制)。
6、运行时常量池
运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池信息(用于存放编译期生成的各种字面量和符号引用)。
既然运行时常量池时方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。
JDK1.7及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。


浙公网安备 33010602011771号