不能不了解的 Java 内存模型
1. Java 内存区域
堆、非堆、Java 虚拟机栈(线程私有)、本地方法栈(线程私有)、程序计数器(线程私有)
堆 | 非堆 | 虚拟机栈 | 本地方法栈 | 程序计数器 | |
---|---|---|---|---|---|
英文名称 | Heap | Non-Heap | JVM Stack | Native Method Stack | Program Counter Register |
说明 | 包含新生代与老年代 | 又称永久代或方法区 | 为 Java 方法执行服务 | 为 Native 方法执行服务 | 字节码执行时的行号计数器 |
线程安全 | 多线程共享 | 多线程共享 | 线程私有 | 线程私有 | 线程私有 |
存储数据 | 对象实例 | 常量、静态变量、已被JVM加载的类 | 栈帧 | 栈帧 | 字节码执行时的行号计数器 |
Windows 下,WIN + R 输入 jconsole 可查看进程下多线程内存分配使用情况
1.1 程序计数器(线程私有)
- 设计
JVM 的多线程执行,通过轮流切换分配逻辑处理器时间的方式来实现的。
同一时刻,一个逻辑处理器执行一个线程中的指令,线程切换时,为了恢复正确的线程执行指令位置。
存在较小的一块内存空间,来作为某个线程的字节码执行时的行号计数器,称之为程序计数器。
1.2 虚拟机栈(线程私有)
- 创建
执行 Java 方法时创建 - 存储
操作数栈,动态链接,方法出口,局部变量表
(局部变量表:编译期可知的基本数据类型,对象的引用,指向字节码指令的地址) - 设计
描述方法执行的内存模型;又可以说,是方法执行时的基础数据结构;
每个方法执行时,会创建一个栈帧
存放局部变量表,操作数栈,动态链接,方法出口等信息;
栈帧在虚拟机栈,从入栈到出栈的过程,即对应着方法调用到执行完成的过程
1.3 本地方法栈(线程私有)
- 创建
执行 native 方法时创建 - 设计
服务于虚拟机执行 native 方法时
1.4 堆
- 创建
虚拟机启动时创建 - 存储
存放对象实例
- 设计
几乎所有的对象实例,在堆内存区域进行内存分配;GC 主要发生在堆内存区域。 - 细分
新生代和老年代;按对象存活周期区分 - 再细分
Eden 区域,From Survivor 区域,To Survivor 区域;
HotSpot 设计上内存分配占比如下Eden : From Survivor : To Survivor = 8 : 1 : 1
;新生代对象垃圾回收算法采用的是复制算法;老年代垃圾回收算法为标记-整理算法;学会了城市垃圾分类,Java 垃圾回收机制我却还不会堆中区域划分是为了更好的回收内存和分配内存;比如对应堆存储分为新生代,老年代来说,非堆存储永久代
1.5 非堆
方法区又称非堆,又可称为永久代
- 存储
已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等
- 设计
同为线程共享区域,取名非堆,即为了与堆区分开;
2. 补充说明
2.1 运行时常量池
- 归属
非堆 - 存储
编译期间生成的各种字面量和符号引用,运行期间生成的新常量。
类加载后进去方法区的运行时常量池。
2.2 运行时数据区域
- 设计
Java 内存区域,JVM 执行程序时的数据区域;划分为新生代,老年代,永久代,虚拟机栈,本地方法栈,程序计数器。
2.3 非 Java 堆
Java 内存区域的堆,称为 Java 堆;非 Java 堆又称为 native 堆
- 归属
不属于 Java 内存区域 - 设计
Java 堆的数据传输到外界,需要把 Java 堆复制到非 Java 堆。
2.4 直接内存
- 归属
不属于 JVM 内存区域 - 设计
IO(Input/Output) 中的通道与缓冲区可以使用 Native 库直接分配堆外内存,这块堆外内存称为直接内存。
Java 堆的 DirectByteBuffer 对象可以引用直接内存进行操作,避免了 Java 堆与 Native 堆来回复制数据。
2.5 小结
(1) 程序计数器,Java 虚拟机栈,本地方法栈是线程私有,不存在线程安全问题;
(2) JVM 内存模型中仅程序计数器不会出现 OutOfMemoryError;
(3) Java 虚拟机栈是为 JVM 执行 Java 方法服务的;本地方法栈是为 JVM 执行 native 方法服务的;
(4) Java 虚拟机栈和本地方法栈会出现 OutOfMemoryError 和 StackOverflowError;
(5) 堆中区域划分是为了更好的回收内存和分配内存;
(6) native 库采用 C/C++ 来实现;
(7) Native Method Stack 属于 Java 内存区域;Native Heap 属于 Native 库;(这个理解不正确的话,欢迎指出问题评论区探讨下 Native 堆)
(8) Java 内存区域,JVM 执行程序时的数据区域;划分为新生代,老年代,永久代,虚拟机栈,本地方法栈,程序计数器;
此图收尾,经典谢幕。
Power By niaonao