JVM内存模型
-
JVM,即Java虚拟机,其内存空间分为五个部分,分别为:
- 程序计数器 (线程私有)
- Java虚拟机栈 (线程私有)
- 本地方法栈 (线程私有)
- 堆 (线程共享)
- 方法区 (线程共享)
-
程序计数器
- 需要注意的是,与计算机中真正的程序计数器PC不同,JVM中的程序计数器是线程私有的,即每条线程都有一个程序计数器。
- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,线程切换回来时通过程序计数器得知当前线程运行到了哪里。
- 如果当前线程正在执行的是一个本地方法,那么此时程序计数器为空。
-
Java虚拟机栈
-
Java虚拟机栈是描述Java方法运行过程的内存模型。
-
当一个方法即将被运行时,Java虚拟机栈首先会在Java虚拟机栈中为该方法创建一块“栈帧”,栈帧中包含局部变量表、操作数栈、动态链接、方法出口信息等。当方法在运行过程中需要创建局部变量时,就将局部变量的值存入栈帧的局部变量表中。
-
局部变量表:局部变量表(Local Variable Table)是一组变值存储空间,用于存放方法参数和方法内部定义的局部变量。作用上基本相当于计算机系统中的栈帧的主要部分,即局部变量的存储和修改。局部变量表所需的容量大小是在编译期确定下来的,在方法运行期间是不会改变局部变量表的大小的。
-
操作数栈:主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。操作数栈就是JVM执行引擎的一个工作区,当一个方法刚开始执行的时候,这个方法的操作数栈是空的。
-
动态链接:在一个class文件中,一个方法要调用其他方法,需要将这些方法的符号引用(即方法名)转化为其在内存地址中的直接引用(即内存地址),而符号引用存在于方法区中的运行时常量池。Java虚拟机栈中,每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,持有这个引用的目的是为了支持方法调用过程中的动态连接。(//…如何支持的动态链接?)
-
-
当这个方法执行完毕后,这个方法所对应的栈帧将会出栈,并释放内存空间。
-
-
本地方法(native method)栈
-
什么是“本地方法”:
-
简单地讲,一个Native Method就是一个java调用非java代码的接口。
-
在定义一个native method时,并不提供实现体,因为其实现体是由非java语言在外面实现的。例:
public class Test{ native public void Native1(int x); native synchronized private float Native3(Object o) ; }
-
如果一个方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针。这些实现在一些DLL文件(Windows系统)内,但是它们会被操作系统加载到java程序的地址空间。
-
本地方法地方法保存在动态链接库中,即.dll(windows系统)文件中,格式是各个平台专有的。故JAVA方法是与平台无关的,但是本地方法不是。(JAVA中有两种方法:JAVA方法和本地方法)
-
-
本地方法栈和Java虚拟机栈实现的功能类似,只不过本地方法区是本地方法运行的内存模型。
-
本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
-
-
堆
-
堆是用来存放对象的内存空间,几乎所有的对象都存储在堆中(JVM可能会通过逃逸分析技术,对于逃不出方法的对象,会让其在栈空间上进行分配。)。
-
堆是垃圾回收的主要场所,并继续细分为新生代、老年代等不同区域,不同的区域存放具有不同生命周期的对象。这样可以根据不同的区域使用不同的垃圾回收算法,从而更具有针对性,从而更高效。
-
堆的大小既可以固定也可以扩展,但主流的虚拟机堆的大小是可扩展的,因此当线程请求分配内存,但堆已满,且内存已满无法再扩展时,就抛出OutOfMemoryError
-
-
方法区
-
Java虚拟机规范中定义方法区是堆的一个逻辑部分。
-
方法区中的信息一般需要长期存在,而且它又是堆的逻辑分区,因此用堆的划分方法,我们把方法区称为老年代。对方法区的内存回收的主要目标是:对常量池的回收和对类型的卸载。
-
方法区中存放三种数据:类信息、常量、静态变量、即时编译器编译后的代码。其中常量存储在运行时常量池中。
-
运行时常量池:
-
-
直接内存:直接内存是除Java虚拟机之外的内存,但也有可能被Java使用。
参考资料:
深入理解JVM(一)——JVM内存模型_凌澜星空的博客-CSDN博客_jvm内存模型
JVM-本地方法_xlshi1996的博客-CSDN博客_jvm如何执行本地方法