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

  • Java 内存模型,描述的是多线程允许的行为
  • JVM 内存结构,描述的是线程运行所设计的内存空间

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

JVM 体系结构

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

如上图所示,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 内存结构

常说的 JVM 内存结构指的就是上文提交到运行时数据区,其中方法区线程共享程序计数器运行时常量池线程独享

它描述的是,在运行时,字节码和代码数据存储的位置。

内存模型

先抛开 Java 不说,先来看下内存模型是什么?维基百科中的定义:

In computing, a memory model describes the interactions of threads through memory and their shared use of the data.

意思就是,在计算中,内存模型描述了多线程如何正确的通过内存进行交互和使用共享数据。换句话说,内存模型约束了处理器对内存的读写。

CPU 和内存之间通常会存在一层或多层高速缓存,这对单处理器可能没问题,但在多处理器系统中,可能就会出现缓存一致性问题,也就是当两个处理器(线程)同时读取相同内存位置会发生什么?什么情况下会看到相同的值?

缓存一致性问题,在并发编程中,又被称作可见性问题。内存模型处理器级别,为处理器彼此之间对内存写入结果的可见性,定义了充分必要条件:

  • 强内存模型,一般说的是顺序一致性,所有内存操作存在一个全序关系,每个操作都是原子的且立即对所有处理器可见
  • 弱内存模型,不限制处理器的内存操作顺序,而使用特殊指令刷新或者使本地缓存失效,以便看到其他处理器的写入,或使此处理器的写入对其他处理器可见,这些特殊指令被称为内存屏障

大多数处理器不会限制内存操作的顺序,多线程在执行时可能会出现让人困惑和违背直觉的结果。这是因为 CPU 为了充分利用不同类型存储器(寄存器、高速缓存、主存)的总线带宽,会将内存操作重新排序,以无序执行,这个动作称为内存排序指令重排序

重排序,也被称为编译器优化和处理器优化,因为它既可以发生在编译期间,也可以发生在 CPU 运行时。为了保证多线程的有序性,需要使用内存屏障禁止重排序

所以说,内存模型就是在硬件层面描述了使用内存屏障(刷新缓存或禁用指令重排序)解决多线程编程中的可见性有序性的问题。

Java 内存模型

Java 内存模型(下文简称 JMM)就是在底层处理器内存模型的基础上,定义自己的多线程语义。它明确指定了一组排序规则,来保证线程间的可见性。

这一组规则被称为 Happens-Before, JMM 规定,要想保证 B 操作能够看到 A 操作的结果(无论它们是否在同一个线程),那么 A 和 B 之间必须满足 Happens-Before 关系

  • 单线程规则:一个线程中的每个动作都 happens-before 该线程中后续的每个动作
  • 监视器锁定规则:监听器的解锁动作 happens-before 后续对这个监听器的锁定动作
  • volatile 变量规则:对 volatile 字段的写入动作 happens-before 后续对这个字段的每个读取动作
  • 线程 start 规则:线程 start() 方法的执行 happens-before 一个启动线程内的任意动作
  • 线程 join 规则:一个线程内的所有动作 happens-before 任意其他线程在该线程 join() 成功返回之前
  • 传递性:如果 A happens-before B, 且 B happens-before C, 那么 A happens-before C

怎么理解 happens-before 呢?如果按字面意思,比如第二个规则,线程(不管是不是同一个)的解锁动作发生在锁定之前?这明显不对。happens-before 也是为了保证可见性,比如那个解锁和加锁的动作,可以这样理解,线程1释放锁退出同步块,线程2加锁进入同步块,那么线程2就能看见线程1对共享对象修改的结果。

Java 提供了几种语言结构,包括 volatile, finalsynchronized, 它们旨在帮助程序员向编译器描述程序的并发要求,其中:

  • volatile - 保证可见性有序性
  • synchronized - 保证可见性有序性; 通过管程(Monitor)保证一组动作的原子性
  • final - 通过禁止在构造函数初始化给 final 字段赋值这两个动作的重排序,保证可见性(如果 this 引用逃逸就不好说可见性了)

编译器在遇到这些关键字时,会插入相应的内存屏障,保证语义的正确性。

有一点需要注意的是,synchronized 不保证同步块内的代码禁止重排序,因为它通过锁保证同一时刻只有一个线程访问同步块(或临界区),也就是说同步块的代码只需满足 as-if-serial 语义 - 只要单线程的执行结果不改变,可以进行重排序。

所以说,Java 内存模型描述的是多线程对共享内存修改后彼此之间的可见性,另外,还确保正确同步的 Java 代码可以在不同体系结构的处理器上正确运行。

小结

它们之间的关系可以这样来个总结,实现一个 JVM 要满足内存结构描述的组成部分,设计如何执行多个线程的时候,要满足Java 内存模型约定的多线程语义。

转载于:https://www.cnblogs.com/wskwbog/p/11349042.html

Java 内存模型和 JVM 内存结构真不是一回事相关推荐

  1. 快速带你分清java内存结构,java内存模型,java对象模型和jvm内存结构!

    现如今你是否有这样的感觉,无论生活还是学习,节奏都是非常的快,每天面对海量的知识信息,自己感觉都要hold不住了,每天打开微信公众号,是不是发现有几十条未读,无论是技术文章还是其他类型的文章,我们大多 ...

  2. 克服焦虑--图解JVM内存模型和JVM线程模型

    前言 各位亲爱的读者朋友,我正在创作 Java多线程系列 文章,严格的说,JVM内存模型的知识并不隶属于Java多线程范畴,但在讨论多线程的过程中,会涉及到相关概念,考虑到它作为 面经常客,故单独成篇 ...

  3. java内存 海子_Java虚拟机:JVM内存模型和volatile详解

    JVM内存模型和volatile详解 Java内存模型 随着计算机的CPU的飞速发展,CPU的运算能力已经远远超出了从主内存(运行内存)中读取的数据的能力,为了解决这个问题,CPU厂商设计出了CPU内 ...

  4. java 堆内存结构_JVM内存结构、Java内存模型和Java对象模型

    JVM内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途.其中有些区域随着虚拟机进程的启动而存 ...

  5. jvm面试2 jvm如何加载java代码? JVM知识重点:内存模型和GC

    jvm如何加载java代码? native方法forName0 JVM知识重点:内存模型和GC' 注意:jvm是一个内存中的虚拟机 下面是Class类中,我们常用的forName方法 @CallerS ...

  6. Java基础:由JVM内存模型详解线程安全

    1.前言 最近在研究JVM内存模型和Java基础知识.主要讲的是线程共享变量与线程私有变量以及如何写出线程安全的代码.这里列出一条规则,"类中的成员变量,也叫实例变量,也叫全局变量,它是非线 ...

  7. jvm内存模型和java内存模型_JVM运行时内存模型综述

    JVM内存模型 JVM分为五个区域:虚拟机栈.本地方法栈.方法区.堆.程序计数器. JVM不同区域的占用内存大小不同,一般情况下堆最大,程序计数器较小. JVM五个区中虚拟机栈.本地方法栈.程序计数器 ...

  8. Android工程师进阶第一课 夯实Java基础 JVM内存模型和GC回收机制

    开篇词:跳出舒适区,走在Android行业的前端 你好,我是姜新星,一个深耕 Andorid 领域的老工程师. 记得 2010 年毕业典礼上,某位老师说"你们是最幸福的一届毕业生,正好赶上中 ...

  9. java gc内存_Java内存模型和GC机制

    最近学习Python的GC机制时,想到了java的GC,忘得差不多了,(⊙﹏⊙)b!!这里便做一下回顾总结.推荐周志明译本的<深入理解Java虚拟机>. 1. Java内存模型 1.1 程 ...

最新文章

  1. Jquery创建JSON对象
  2. 你会选择深圳还是佛山?
  3. Mac版钉钉之一个新的技术electron
  4. 判断字符串最后两个字符
  5. python识别颜色1007python识别颜色_python读取word文档识别字段颜色,解析字段
  6. Android程序设置软件图标
  7. spring 读取配置文件
  8. 【算法工程师】华为技术面面试记录
  9. Sql server添加示例数据库Northwind
  10. win8计算机休眠的区别,电脑的待机/关机/睡眠和休眠有什么区别?Win8开机为什么那么快?...
  11. 计算机辅助翻译在翻译实践中的应用,计算机辅助翻译在翻译实践中的应用
  12. 团队中各成员间相互协作办公用哪一个软件
  13. MS5611气压计数据采集(模拟IIC)/温度采集/相对高度求解
  14. css怎么两线合并,【2人回答】CAD中如何把两条线合并成一条线?-3D溜溜网
  15. html如何创建邮件链接,如何制作电子邮件链接html
  16. java selector 源码_Java NIO——Selector机制源码分析---转
  17. 操作系统面试——虚拟内存
  18. IMU传感器时什么?(二)陀螺仪的种类和原理
  19. 怎样设计访谈提纲_如何设计采访提纲
  20. vs2022运行显示找不到exe文件

热门文章

  1. c标签 if else
  2. POJ 2800 垂直直方图 解题报告
  3. 手机被偷后如何让小偷不能用
  4. 69.数据库系统的两级映射(映像)
  5. 多线程编程之死锁已经死锁产生的原因
  6. eclipse没有日志_IPFS技术最新进展:抵抗eclipse攻击的能力
  7. 001_Spring概述
  8. 将mysql日期格式转换_如何将日期时间格式转换为mysql日期格式?
  9. xgboost 正则项_深入理解Boosting算法(4)-XGBoost
  10. 启动zookeeper_Zookeeper原理篇-Zookeeper启动流程分析