当前位置: 首页 > news >正文

JVM 主副内存 详解

在 JVM (Java Virtual Machine) 中,内存的设计主要分为主内存和工作内存(又称为线程内存)。这种设计是基于 Java 内存模型(Java Memory Model, JMM) 的规定,它确保了多线程环境下数据的一致性和线程间的通信。


1. 主内存与工作内存的概念

1.1 主内存

  • 主内存是所有线程共享的内存区域,主要存储程序中所有的实例对象和类变量
  • 它对应于 JVM 堆内存(Heap)和方法区(Method Area)。
  • 主内存中的数据是线程共享的,因此线程之间通过主内存通信。

1.2 工作内存

  • 工作内存是线程的私有区域,用于存储线程从主内存中拷贝的数据的副本。
  • 它对应于线程栈(Thread Stack),存放线程独立的局部变量、操作栈和部分对象引用。
  • 每个线程只能访问自己的工作内存,不能直接操作其他线程的工作内存。

2. 主内存与工作内存的关系

主内存和工作内存的关系类似于共享内存与高速缓存之间的关系:

  1. 线程对变量的所有操作(读取、写入)都必须先在工作内存中进行。

    • 线程从主内存将变量值读取到工作内存中。
    • 对变量的修改也会先在工作内存中完成,然后再同步回主内存。
  2. 线程不能直接操作主内存中的变量,所有变量必须通过工作内存中转。

示意图

主内存 (共享变量)↑   ↓
工作内存 (线程1)↑   ↓
工作内存 (线程2)

3. 主内存与工作内存的交互

JMM 定义了一组原子操作来完成主内存与工作内存之间的交互:

操作描述
lock把主内存中的变量标记为线程独占状态。
unlock解除对主内存变量的独占状态,释放给其他线程使用。
read从主内存中读取变量值到工作内存。
load把工作内存中的变量加载到线程的工作内存中。
use把工作内存中变量的值传递给执行引擎。
assign把执行引擎的值赋值给工作内存中的变量。
store把工作内存中的变量值写回主内存。
write把主内存的变量值更新为工作内存中的值。

交互流程

以变量 x 为例:

  1. 读取过程:线程从主内存中 read x,然后 load x 到工作内存。
  2. 操作过程:线程在工作内存中 use xassign x 进行计算。
  3. 写入过程:线程将工作内存中 store x,然后 write x 更新到主内存。

4. 主内存和工作内存的特点

4.1 主内存

  • 线程共享:主内存是所有线程共享的,用于存储全局变量、类变量和堆上的对象。
  • 数据一致性:主内存是线程间通信的桥梁,所有线程对共享变量的修改都必须最终同步到主内存。

4.2 工作内存

  • 线程私有:工作内存是每个线程独立的,用于存储线程从主内存中拷贝的变量值。
  • 临时存储:工作内存中的变量值只是主内存的一个副本,线程操作完成后需要同步回主内存。
  • 提高效率:减少线程对主内存的频繁访问。

5. 主内存与工作内存的典型问题

5.1 可见性问题

  • 如果一个线程修改了变量的值,但没有及时刷新到主内存,其他线程无法感知到最新的变量值。
  • 示例
    public class VisibilityExample {private static boolean flag = true;public static void main(String[] args) {new Thread(() -> {while (flag) {// 如果flag没有及时刷新到主内存,该线程可能无法退出循环}}).start();new Thread(() -> {flag = false; // 修改flag值,但未及时刷新到主内存}).start();}
    }
    

5.2 指令重排序问题

  • JVM 或 CPU 可能会对代码的执行顺序进行优化,导致线程看到的操作顺序与程序代码不一致。
  • 示例
    public class ReorderingExample {private static boolean flag = false;private static int value = 0;public static void main(String[] args) {new Thread(() -> {value = 42; // 可能先执行flag = true;}).start();new Thread(() -> {if (flag) {System.out.println(value); // 可能输出 0 而不是 42}}).start();}
    }
    

5.3 解决方法

  • 使用 volatile
    • 保证变量的可见性和禁止指令重排序。
  • 使用同步机制
    • 通过 synchronized 或锁机制来确保线程间的同步。

6. 主内存与 JVM 内存结构的关系

主内存主要对应于以下 JVM 内存区域:

  1. 堆内存(Heap)
    • 存储对象实例,所有线程共享。
  2. 方法区(Method Area)
    • 存储类信息、常量池和静态变量,所有线程共享。

工作内存主要对应于以下 JVM 内存区域:

  1. 线程栈(Thread Stack)
    • 存储局部变量和操作栈,每个线程独立。
  2. 程序计数器(Program Counter)
    • 跟踪当前线程执行到的字节码指令,每个线程独立。

7. 实际应用中的主内存与工作内存

7.1 可见性问题与 volatile

volatile 关键字确保变量的修改对所有线程可见,防止工作内存中的值与主内存不一致。

示例:
public class VolatileExample {private static volatile boolean flag = true;public static void main(String[] args) {new Thread(() -> {while (flag) {// 保证线程可以感知flag的最新值}}).start();new Thread(() -> {flag = false; // 修改flag值}).start();}
}

7.2 同步问题与 synchronized

synchronized 确保线程对共享资源的访问是互斥的,且修改后的变量会立即同步到主内存。

示例:
public class SynchronizedExample {private static int counter = 0;public static synchronized void increment() {counter++;}public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) increment();});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) increment();});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Counter: " + counter); // 输出: 2000}
}

8. 总结

  • 主内存:存储共享数据,所有线程可访问。
  • 工作内存:线程私有,存储主内存变量的副本。
  • 典型问题:可见性问题、指令重排序、竞态条件。
  • 解决方法:使用 volatile 保证可见性,使用 synchronized 保证原子性和可见性。

这种主内存-工作内存的模型是 Java 内存模型的核心,帮助开发者在多线程环境下编写安全的并发程序。

http://www.lryc.cn/news/496927.html

相关文章:

  • sscanf与sprintf函数
  • 【k8s】创建基于sa的token的kubeconfig
  • Gentoo Linux部署LNMP
  • 2411C++,CXImage简单使用
  • 什么是 Kubernetes(K8s)?
  • 深入解析:TypeScript 与 Vue 的完美结合
  • 机器学习周志华学习笔记-第13章<半监督学习>
  • 软件工程——期末复习(1)
  • 【JavaEE初阶 — 网络编程】实现基于TCP协议的Echo服务
  • vue结合canvas动态生成水印效果
  • Qt 5 中的 QTextStream 使用指南
  • 中安证件OCR识别技术助力鸿蒙生态:智能化证件识别新体验
  • SpringBoot 框架下基于 MVC 的高校办公室行政事务管理系统:设计开发全解析
  • 【ArkTS】使用AVRecorder录制音频 --内附录音机开发详细代码
  • Selenium3+Python如何操作键盘
  • PLC协议
  • C_字符串的一些函数
  • 使用Native AOT发布C# dll 提供给C++调用
  • Git 提交代码日志信息
  • Request method ‘POST‘ not supported(500)
  • 终端环境下关闭显示器
  • 常见排序算法总结 (三) - 归并排序与归并分治
  • 【后端开发】Go语言编程实践,Goroutines和Channels,基于共享变量的并发,反射与底层编程
  • PyTorch 2.5.1: Bugs修复版发布
  • 【Android】组件化嘻嘻嘻gradle耶耶耶
  • vulnhub靶场【哈利波特】三部曲之Aragog
  • HarmonyOS开发中,如何高效定位并分析内存泄露相关问题
  • java调用ai模型:使用国产通义千问完成基于知识库的问答
  • 2023年第十四届蓝桥杯Scratch国赛真题—推箱子
  • 银河麒麟V10-SP1设置redis开机自启