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 内存模型的核心,帮助开发者在多线程环境下编写安全的并发程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/928335.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch在liunx 中单机部署

下载配置 1、下载 官网下载地址 2、上传解压 tar -zxvf elasticsearch-XXX.tar.gz 3、新建组和用户 &#xff08;elasticsearch 默认不允许root账户&#xff09; #创建组 es groupadd es #新建用户 useradd ryzhang -g es 4、更改文件夹的用户权限 chown -R ryzhang …

基于 MVC 的 SpringBoot 高校行政事务管理系统:设计思路与实现步骤详解

2系统开发环境 2.1vue技术 Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 [5] 与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第…

不敢相信,Nginx 还能这么玩?

大家好&#xff0c;我是程序员鱼皮。今天来聊聊 Nginx 技术&#xff0c;这是一个企业项目必用&#xff0c;但是却经常被程序员忽略的技术。学好 Nginx&#xff0c;可以助你在求职中脱颖而出。 或许你会想&#xff1a;“Nginx 不就是用来部署网站的服务器嘛&#xff1f;这有何难…

【AI系统】指令和存储优化

指令和存储优化 除了应用极广的循环优化&#xff0c;在 AI 编译器底层还存在指令和存储这两种不同优化。 指令优化 指令优化依赖于硬件提供的特殊加速计算指令。这些指令&#xff0c;如向量化和张量化&#xff0c;能够显著提高计算密度和执行效率。向量化允许我们并行处理数…

底部导航栏新增功能按键

场景需求&#xff1a; 在底部导航栏添加power案件&#xff0c;单击息屏&#xff0c;长按 关机 如下实现图 借此需求&#xff0c;需要掌握技能&#xff1a; 底部导航栏如何实现新增、修改、删除底部导航栏流程对底部导航栏部分样式如何修改。 比如放不下、顺序排列、坑点如…

基于Matlab卡尔曼滤波的GPS/INS集成导航系统研究与实现

随着智能交通和无人驾驶技术的迅猛发展&#xff0c;精确可靠的导航系统已成为提升车辆定位精度与安全性的重要技术。全球定位系统&#xff08;GPS&#xff09;和惯性导航系统&#xff08;INS&#xff09;在导航应用中各具优势&#xff1a;GPS提供全球定位信息&#xff0c;而INS…

Jenkins升级到最新版本后无法启动

1. 场景还原 最近在web界面将jenkins升级到最新版本后&#xff0c;后台无法启动jenkins服务&#xff0c;服务状态如下&#xff1a; 运行jenkins命令提示invalid Java version jenkins --version jenkins: invalid Java version: java version "1.8.0_202" Java(TM)…

shell编程 1 (泷羽sec)

声明 学习视频来自B站UP主 泷羽sec,如涉及侵泷羽sec权马上删除文章。 笔记只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 这节课旨在扩大自己在网络安全方面的知识面&#xff0c;了解网络安全领域的见闻&#xff0c;了…

威联通-001 手机相册备份

文章目录 前言1.Qfile Pro2.Qsync Pro总结 前言 威联通有两种数据备份手段&#xff1a;1.Qfile Pro和2.Qsync Pro&#xff0c;实践使用中存在一些区别&#xff0c;针对不同备份环境选择是不同。 1.Qfile Pro 用来备份制定目录内容的。 2.Qsync Pro 主要用来查看和操作文…

【机器学习】分类任务: 二分类与多分类

二分类与多分类&#xff1a;概念与区别 二分类和多分类是分类任务的两种类型&#xff0c;区分的核心在于目标变量&#xff08;label&#xff09;的类别数&#xff1a; 二分类&#xff1a;目标变量 y 只有两个类别&#xff0c;通常记为 y∈{0,1} 或 y∈{−1,1}。 示例&#xff…

GaussDB(类似PostgreSQL)常用命令和注意事项

文章目录 前言GaussDB&#xff08;类似PostgreSQL&#xff09;常用命令和注意事项1. 连接到GaussDB数据库2. 查看当前数据库中的所有Schema3. 进入指定的Schema4. 查看Schema下的表、序列、视图5. 查看Schema下所有的表6. 查看表结构7. 开始事务8. 查询表字段注释9. 注意事项&a…

点灯大师——WIFI控制灯

在之前的教程中&#xff0c;我们学习了 ESP6266 的原理&#xff0c;并动手写了驱动&#xff0c;实现了串口的通讯和 STA、AP、STAAP 三种模式。本次我们就来教大家如何使用 ESP8266 控制灯。这是一个简单的示例&#xff0c;展示了如何将 WIFI 通信与硬件控制相结合&#xff0c;…

如何使用brew安装phpredis扩展?

如何使用brew安装phpredis扩展&#xff1f; phpredis扩展是一个用于PHP语言的Redis客户端扩展&#xff0c;它提供了一组PHP函数&#xff0c;用于与Redis服务器进行交互。 1、cd到php某一版本的bin下 /usr/local/opt/php8.1/bin 2、下载 phpredis git clone https://githu…

Android 使用OpenGLES + MediaPlayer 获取视频截图

概述 Android 获取视频缩略图的方法通常有: ContentResolver: 使用系统数据库MediaMetadataRetriever: 这个是android提供的类&#xff0c;用来获取本地和网络media相关文件的信息ThumbnailUtils: 是在android2.2&#xff08;api8&#xff09;之后新增的一个&#xff0c;该类为…

面向对象(二)——类和对象(上)

1 类的定义 做了关于对象的很多介绍&#xff0c;终于进入代码编写阶段。 本节中重点介绍类和对象的基本定义&#xff0c;属性和方法的基本使用方式。 【示例】类的定义方式 // 每一个源文件必须有且只有一个public class&#xff0c;并且类名和文件名保持一致&#xff01; …

echarts的双X轴,父级居中的相关配置

前言&#xff1a;折腾了一个星期&#xff0c;在最后一天中午&#xff0c;都快要放弃了&#xff0c;后来坚持下来&#xff0c;才有下面结果。 这个效果就相当是复合表头&#xff0c;第一行是子级&#xff0c;第二行是父级。 子级是奇数个时&#xff0c;父级label居中很简单&…

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测(Maltab)

顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09; 目录 顶刊算法 | 鱼鹰算法OOA-BiTCN-BiGRU-Attention多输入单输出回归预测&#xff08;Maltab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…

Agile VMO分享:海尔案例

海尔集团是全球最大的家电制造商之一&#xff0c;拥有超过76 000名员工。它获得了2018-2019年全球智能家电品牌前10名和2018-2019年全球消费电子品牌前50名的荣誉。 海尔利用价值流结构将自己组织成一些可以自管理的微型企业。这些微型企业拥有决策&#xff0c;设计和交付新产品…

第七课 Unity编辑器创建的资源优化_UI篇(UGUI)

上期我们学习了简单的Scene优化&#xff0c;接下来我们继续编辑器创建资源的UGUI优化 UI篇&#xff08;UGUI&#xff09; 优化UGUI应从哪些方面入手&#xff1f; 可以从CPU和GPU两方面考虑&#xff0c;CPU方面&#xff0c;避免触发或减少Canvas的Rebuild和Rebatch&#xff0c…

LabVIEW MathScript工具包对运行速度的影响及优化方法

LabVIEW 的 MathScript 工具包 在运行时可能会影响程序的运行速度&#xff0c;主要是由于以下几个原因&#xff1a; 1. 解释型语言执行方式 MathScript 使用的是类似于 MATLAB 的解释型语言&#xff0c;这意味着它不像编译型语言&#xff08;如 C、C 或 LabVIEW 本身的 VI&…