Java内存区域
Java内存区域就是Java运行时数据区
- 线程私有的:程序计数器、虚拟机栈、本地方法栈
- 线程共享的:堆、方法区
1.程序计数器
程序计数器是什么:可以看作是当前线程所执行的字节码的行号指示器
程序计数器的作用:
- 实现代码的流程控制
- 确保线程切回时抵达正确位置
2.虚拟机栈
栈是 JVM 运行时数据区域的一个核心,除了一些 Native 方法是本地方法栈实现的
其他所有 Java 方法的调用都是由虚拟机栈协助执行的
虚拟机栈由一个个栈帧组成,栈帧的组成有:
- 局部变量表
- 操作数栈
- 动态链接
- 方法返回地址
接下来我们一个个说明他们的作用
局部变量表
局部变量表用来存放:
- 方法的this对象
- 方法的参数
- 方法内部定义的局部变量
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译成Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的最大局部变量表的容量。
操作数栈
操作数栈也是栈结构,可以简单的理解它的作用就是方法中变量进行运算
动态链接
主要服务一个方法调用另一个方法的场景
动态链接的作用就是为了将符号引用转换为直接引用
方法返回
无论方法采用何种方式退出,在方法退出后都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在当前栈帧中保存一些信息,用来帮他恢复它的上层方法执行状态。
方法退出过程实际上就等同于把当前栈帧出栈,因此退出可以执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者的操作数栈中,调整PC计数器的值以指向方法调用指令后的下一条指令。
一般来说,方法正常退出时,调用者的PC计数值可以作为返回地址,栈帧中可能保存此计数值。而方法异常退出时,返回地址是通过异常处理器表确定的,栈帧中一般不会保存此部分信息。
3.本地方法栈
调用Native方法
4.堆
堆是线程共享的。
JDK7版本以及JDK7版本之前,堆内存通常分为下面三部分:
- 新生代(年轻代)内存
- 老年代
- 永久代
下图中的Eden区、两个S区S0、S1都属于新生代
JDK8版本之后永久代被元空间取代,元空间使用的是直接内存
元空间,不再与堆连续,而是直接存在于本地内存,也就是机器的内存。理论上机器内存有多大,元空间就有多大。
在之前的版本中,字符串常量池存在于永久代中,在大量使用字符串的情况下,容易出现OOM异常,此外JVM加载的class总数、方法的大小等都很难确定,因此对永久代大小难以确定。
5.方法区
当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
主要是存放一个类的:
基础信息
常量池
字段
方法
法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
主要是存放一个类的:
基础信息
常量池
字段
方法
属性