概述:
内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请,分配,管理的策略,保证了JVM的高效稳定运行。不同的JVM对于内存的划分方式和管理机制存在着部分差异。结合JVM虚拟机规范,来探讨一下经典的JVM内存布局
每个线程:独立包括程序计数器,本地方法栈,虚拟机栈(每个线程独立的一份)
线程间共享:方法区,堆区。
1.程序计数器
JVM中的程序计数寄存器中,Register的命名源于CPU的寄存器,寄存器存储指令相关的现场信息。CPU只有把数据装载到寄存器才能够运行。
也可以将其翻译为PC计数器(或将其翻译为PC计数器)会更加贴切(也称为程序钩子),并且也不容易 引起一些不必要的误会。JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟。
注意:程序计数器没有 GC 和OOM (前者垃圾回收机制,后者内存溢出)
2.虚拟机栈
由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。
有点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。
2.1 栈是运行时的单位,而堆是存储的单位。
即:栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放,放在哪儿。
2.2 什么是java虚拟机栈?
java虚拟机栈,早期也叫栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的帧栈,
对应着一次次的java方法调用。是线程私有的。
2.3 作用
主管java程序的运行,它保存方法的局部变量(8种基本数据类型,对象的引用地址),部分结果,并参与方法的调用和返回。
- 局部变量 vs 成员变量(或属性)
- 基本数据类型 vs 引用类型变量(类,数组,接口)
2.4 栈的优点
- 栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器。
- JVM直接对Java栈的操作只有俩个:
- 每个方法执行,伴随着进栈(入栈,压栈)
- 执行结束后的出栈工作
- 对于栈来说不存在垃圾回收问题。》会有OOM(内存溢出)
3. 栈的存储结构和运行原理
3.1 栈中存储什么?
- 每个线程都有自己的栈,栈中的数据都是以栈帧的格式存在。
- 在这个线程上正在执行的每个方法都各自对应一个栈帧
- 栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息。
3.2 原理
- JVM对栈的操作只有俩个,就是对栈的压栈和出栈。
- 在一条活动的线程中,一个时间点上,只有一个活动的栈帧,只有当前正在执行的方法的栈帧是有效的,这个栈帧被称为当前栈帧,与当前栈帧相对应的就是当前方法,定义这个方法的类就是当前类。
- 执行引擎运行的所有字节码指令只针对当前栈帧进行操作。
- 如果在在该方法调用了其它方法,对应新的栈帧会被创建出来,放在栈的顶端,称为新的当前帧。
- 不同线程中所包含的栈帧是不允许在相互引用的,即不可能在一个栈帧之中引用另外一个的栈帧。
- 如果当前方法调用了其它方法,方法返回之际,当前栈帧会传回此方法的返回结果,给前一个栈帧,接着虚拟机就会丢掉当前栈帧,使得前一个栈帧重新成为当前栈帧。
- java方法有俩种返回函数的方式:一种是正常函数返回的,使用return指令。另外一种是抛出异常,不管使用哪种方式,都会导致栈帧被弹出。
3.2 栈帧的内部机构
-
局部变量表
-
操作数栈
-
动态链接(指向运行时常量池的方法引用)
-
方法返回地址
-
一些附加信息
局部变量表结构
- 定义一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量(基本数据类型,对象引用,返回地址.)
- 局部变量表是建立在线程的栈上,是线程私有的,所以不会存在数据安全问题。
- 局部变量表所需的容量大小是在编译期确定下来的。
- 方法嵌套调用的次数,由栈的大小决定。栈越大,方法嵌套调用次数越多。
- 局部变量表中的变量只在当前方法调用有效。方法调用结束,方法栈帧销毁,局部变量表也会随之销毁。
操作数栈
- 操作数栈,在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈和出栈。
- 操作数栈,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
- 操作数栈就是JVM执行引擎的一个工作区,当一个方法开始的时候,这个栈帧也会创建出来,这个方法的操作数栈是空的。
- 操作数栈是通过标准的入栈和出栈来完成一次数据的访问
- 如果被调用的方法有返回值,其返回值将会被压入当前栈帧的操作数栈,并更新PC寄存器指令。
- Java虚拟机的解释引擎是基于栈的执行引擎,其中的栈指的就是操作数栈。
动态链接(指向运行时常量池的方法引用)
- 每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属的方法引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接。
- 在Java源文件被编译到字节码文件中,所有的变量和方法引用都作为符号引用保存在class文件的常量池中。动态链接的作用就是将符号引用转换为调用方法的直接引用。
4.本地方法栈
本地方法栈就是一个Java调用非Java代码的接口。