Skip to content

讲一下JVM的内存模型

JVM内存模型(Java Memory Model,JMM)是Java并发编程的核心基础,也是面试中的高频考点。它主要解决了多线程环境下共享数据的可见性、原子性和有序性问题,定义了线程和主内存之间的抽象关系。

一、JMM的核心目标

  • 解决多线程内存可见性问题:多个线程如何看到彼此的内存修改
  • 规范指令重排序规则:在不影响单线程执行结果的前提下,允许编译器和CPU对指令重排序,但需保证多线程安全
  • 定义happens-before规则:建立操作之间的偏序关系,确保多线程环境下的执行结果可预测

二、JMM的内存抽象模型

JMM将内存划分为两种:

  1. 主内存:所有线程共享的内存区域,存储共享变量(实例字段、静态字段等)
  2. 工作内存:每个线程独有的内存区域,存储共享变量的副本

交互规则

  • 线程对共享变量的操作必须在自己的工作内存中进行,不能直接读写主内存
  • 线程修改后的变量需同步回主内存,其他线程才能看到最新值

三、三大特性及保证机制

1. 原子性(Atomicity)

  • 定义:一个操作或多个操作要么全部执行且执行过程不被中断,要么都不执行
  • 保证方式
    • 基本数据类型的读取和赋值操作是原子性的(long和double除外,在32位系统可能非原子)
    • synchronized关键字:通过同步锁保证代码块的原子性
    • java.util.concurrent.atomic包:提供CAS操作实现原子性(如AtomicInteger)

2. 可见性(Visibility)

  • 定义:当一个线程修改了共享变量的值,其他线程能立即看到修改后的值
  • 保证方式
    • volatile关键字:强制变量修改后立即同步到主内存,读取时直接从主内存加载
    • synchronized:解锁前必须将变量同步回主内存
    • final:被final修饰的变量初始化后不可修改,具有可见性

3. 有序性(Ordering)

  • 定义:程序执行的顺序按照代码的先后顺序执行
  • 问题来源
    • 编译器优化重排序
    • CPU指令重排序
    • 内存系统重排序
  • 保证方式
    • volatile:禁止指令重排序(通过内存屏障)
    • synchronized:保证同一时刻只有一个线程执行同步块,相当于顺序执行
    • happens-before规则:无需任何同步手段即可保证有序性的天然规则

四、Happens-Before规则(高频考点)

JMM定义的天然有序性规则,无需额外同步操作:

  1. 程序顺序规则:单线程中,前面的操作happens-before后面的操作
  2. volatile变量规则:对volatile变量的写操作happens-before后续的读操作
  3. 锁规则:解锁操作happens-before后续的加锁操作
  4. 传递性:若A happens-before B,B happens-before C,则A happens-before C
  5. 线程启动规则Thread.start() happens-before线程中的任何操作
  6. 线程终止规则:线程中的所有操作happens-before其他线程检测到该线程终止
  7. 线程中断规则:对线程的中断操作happens-before被中断线程感知到中断
  8. 对象终结规则:对象的构造函数执行完成happens-before其finalize()方法

五、Volatile关键字的作用(重点)

  1. 保证可见性:变量修改后立即同步到主内存
  2. 禁止指令重排序:通过插入内存屏障实现:
    • 写操作后插入StoreStore屏障,防止后续写操作重排序到前面
    • 写操作后插入StoreLoad屏障,防止读操作重排序到写操作前面
    • 读操作前插入LoadLoad屏障,防止前面的读操作重排序到后面
    • 读操作后插入LoadStore屏障,防止后面的写操作重排序到前面
  3. 不保证原子性:如i++操作仍需同步手段保证原子性

六、常见面试题解析

  1. Q:JMM和JVM内存结构的区别?
    A:JMM是抽象模型,关注多线程内存交互;JVM内存结构是具体划分(如堆、方法区、虚拟机栈等),关注内存分配。

  2. Q:Volatile和Synchronized的区别?
    A:Volatile轻量级,保证可见性和有序性,不保证原子性;Synchronized重量级,保证三大特性,可修饰方法和代码块。

  3. Q:为什么DCL单例模式需要Volatile?
    A:防止指令重排序导致的"半初始化"问题,instance = new Singleton()可能被拆分为分配内存、初始化、赋值三步,重排序后可能导致其他线程获取到未初始化的实例。

JMM是理解Java并发编程的基础,掌握其核心概念(三大特性、happens-before规则、volatile作用)对解决并发问题和应对面试至关重要。

Released under the MIT License.