Java 内存模型和 JVM 内存结构真不是一回事

这两个概念估计有不少人会混淆,它们都可以说是 JVM 规范的一部分,但真不是一回事!它们描述和解决的是不同问题,简单来说,

Java 内存模型,描述的是多线程允许的行为

JVM 内存结构,描述的是线程运行所设计的内存空间

JVM 是什么呢?它屏蔽了底层架构的差异性,是 Java 跨平台的依据,也是每个 Java 程序员必须了解的一部分。

JVM 体系结构

Java Virtual Machine(JVM) 是一种抽象的计算机,基于堆栈架构,它有自己的指令集和内存管理。它加载 class 文件,分析、解释并执行字节码。基本结构如下:

JVM Architecture

如上图所示,JVM 主要分为三个子系统:类加载器运行时数据区执行引擎

类加载器子系统

它主要功能是处理类的动态加载,还有链接,并且在第一次引用类时进行初始化

Loading - 加载,顾名思义,用于加载类,它有三种类加载器,根据双亲委托模型,从不同路径进行加载:

Bootstrap ClassLoader - 加载 rt.jar 核心类库,是优先级最高的加载器

Extension ClassLoader - 负责加载 jre\lib\ext 文件夹中的类

Application ClassLoader -负责加载 CLASSPATH 指定的类库

Linking - 链接,动态链接到运行时所需的资源,分为三步:

Verify - 验证:验证生成的字节码是否正确

Prepare - 准备:为所有静态变量,分配内存并赋予默认值

Resolve - 解析:将 class 文件常量池中所有对内存的符号引用,替换成到方法区的直接引用

Initialization - 类初始化,类加载的最后阶段,这里对静态变量进行赋值,并执行静态块。(注意区分对象初始化

运行时数据区

它约定了在运行时程序代码的数据比如变量、参数等等的存储位置,主要包含以下几部分:

PC 寄存器(程序计数器):保存正在执行的字节码指令的地址

:在方法调用时,创建一个叫栈帧的数据结构,用于存储局部变量和部分过程的结果,栈帧由以下几部分组成:

局部变量表:存储方法调用时传递的参数,从0开始存储this、方法参数、局部变量

操作数栈:执行中间操作,存储从局部变量表或对象实例字段复制的常量或变量值,以及操作结果,另外,还用来准备被调用方法的参数和接受方法调用的返回结果

动态链接:一个指向运行时常量池的引用,将 class 文件中的符号引用(描述一个方法调用了其他方法或访问成员变量)转为直接引用

方法返回地址:方法正常退出或抛出异常退出,返回方法被调用的位置

:存储类实例对象和数组对象,垃圾回收的主要区域

方法区:也被称为元空间,还有个别名 non-heap(非堆),使用本地内存存储 class meta-data 元数据(运行时常量池,字段和方法的数据,构造函数和方法的字节码等),在 JDK 8 中,把 interned String 和类静态变量移动到了 Java 堆

运行时常量池:存储类或接口中的数值字面量字符串字面量以及所有方法或字段的引用,基本上涉及到方法或字段,JVM 就会在运行时常量池中搜索其具体的内存地址

本地方法栈:与 JVM 栈类似,只不过服务于 Native 方法

执行引擎

运行时数据区存储着要执行的字节码,执行引擎将会读取并逐个执行。

Interpreter - 解释器,它对字节码的解释很快,但执行慢,有个缺点是,当方法被多次调用时,每次都需要重新解释。

JIT Compiler- JIT编译器, 解决了解释器的缺点,仍使用解释器来转换字节代码,但发现有代码重复执行时,会使用 JIT 编译器,将整个字节码编译成本地代码,将本地代码用于重复调用,从而提高系统的性能,有以下几部分组成:

中间代码生成器 - 生成中间代码

代码优化器 - 负责优化上面生成的中间代码

目标代码生成器 - 负责生成机器代码或本地代码

Profiler - 一个特殊组件,负责查找热点,判断该方法是否被多次调用

Garbage Collector- 垃圾收集器,收集和删除未引用的对象。

另外,还包括执行引擎所需的本地库(Native Method Libraries)和与其交互的 JNI 接口(Java Native Interface)。

现在来看下 Java 内存模型和 JVM 内存结构有何不同。

JVM 内存结构

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zydfzp.html