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

【JVM深度解析】第24篇:JVM内存模型(JMM)核心原理

摘要JMMJava Memory ModelJava 内存模型是 Java 并发编程的基础它定义了线程之间共享变量的可见性、有序性问题以及如何通过 Happens-Before 规则和内存屏障来解决这些问题。理解 JMM你才能真正明白 volatile、synchronized、final 等关键字的底层原理以及为什么多线程代码如此容易出错。本文深入解析 JMM 的三大核心问题原子性、可见性、有序性以及 Happens-Before 规则、内存屏障、CPU 缓存一致性MESI 协议等底层机制。一、JMM 概述1.1 为什么需要 JMMJMM 解决的核心问题 ┌──────────────────────────────────────────────────────────────────┐ │ │ │ 问题多核 CPU 缓存 指令重排序 并发 Bug 的根源 │ │ │ │ 示例 │ │ Thread A: │ │ flag true; // 写入主内存 │ │ data 42; // 写入 CPU 缓存未刷回 │ │ │ │ Thread B: │ │ if (flag) { // 读取 flag true │ │ System.out.println(data); // 读取到 0 │ │ } │ │ │ │ 结果Thread B 可能输出 0 而不是 42 │ │ │ └──────────────────────────────────────────────────────────────────┘1.2 JMM 的抽象结构┌──────────────────────────────────────────────────────────────────┐ │ JMM 抽象结构 │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 主内存Main Memory │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ shared variables (heap, method area) │ │ │ │ │ │ - 对象字段 │ │ │ │ │ │ - 静态变量 │ │ │ │ │ │ - 数组元素 │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ ↑ load ↑ store │ │ ↓ store ↓ load │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Thread 1 │ │ Thread 2 │ │ Thread 3 │ │ Thread N │ │ │ │ Working │ │ Working │ │ Working │ │ Working │ │ │ │ Memory │ │ Memory │ │ Memory │ │ Memory │ │ │ │ (CPU Cache)│ │ (CPU Cache)│ │ (CPU Cache)│ │ (CPU Cache)│ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ 每个线程有自己的 Working Memory工作内存 │ │ 共享变量先复制到工作内存操作后再写回主内存。 │ │ │ └──────────────────────────────────────────────────────────────────┘二、JMM 三大特性2.1 原子性Atomicity原子性操作要么全部完成要么全部不执行不会被中断。// 原子操作x1;// 原子基本类型赋值objnewObject();// 原子引用赋值// 非原子操作x;// 非原子read-modify-writeobj.field1;// 非原子可能只写了 1 个字节时被打断obj.hashCode();// 非原子内部有多个步骤// synchronized 保证原子性publicsynchronizedvoidincrement(){x;// 整个方法体是原子的}2.2 可见性Visibility可见性一个线程对共享变量的修改能被其他线程及时看到。// 可见性问题publicclassVisibilityDemo{privatebooleanflagfalse;publicvoidwriter(){flagtrue;// 写入主内存// 可能只写入了 CPU 缓存Thread B 看不到}publicvoidreader(){if(flag){// 读取到的是旧值 false// 可能永远不会执行到这里}}}2.3 有序性Ordering有序性程序代码的执行顺序与预期一致但 CPU 可能重排序。// 重排序示例publicclassReorderDemo{privateinta0;privatebooleanflagfalse;publicvoidwriter(){a1;// 操作 1flagtrue;// 操作 2// 编译器/CPU 可能重排序为// flag true;// a 1;}publicvoidreader(){if(flag){// 读取 flag trueSystem.out.println(a);// a 可能还是 0}}}三、Happens-Before 规则3.1 什么是 Happens-BeforeHappens-Before 是 JMM 最核心的概念——它定义了什么时候一个操作 A happens-before 操作 BHappens-Before 的含义 ┌──────────────────────────────────────────────────────────────────┐ │ │ │ A HB BA happens-before B意味着 │ │ │ │ 1. A 的结果对 B 可见 │ │ 2. A 的执行顺序在 B 之前 │ │ │ │ 注意这不意味着 A 一定在 B 之前执行 │ │ 只是保证了如果 A 在 B 之前执行完成结果对 B 可见 │ │ │ └──────────────────────────────────────────────────────────────────┘3.2 JMM 定义的原则Happens-Before 规则 ┌──────────────────────────────────────────────────────────────────┐ │ │ │ 1. 程序顺序规则Program Order Rule │ │ 在同一线程中按代码顺序前面的操作 HB 后面的操作 │ │ │ │ 2. 监视器锁规则Monitor Lock Rule │ │ unlock 操作 HB 后续对同一个锁的 lock 操作 │ │ │ │ 3. volatile 变量规则Volatile Variable Rule │ │ 对 volatile 变量的写 HB 后续对该变量的读 │ │ │ │ 4. 线程启动规则Thread Start Rule │ │ Thread.start() HB 该线程的每个操作 │ │ │ │ 5. 线程终止规则Thread Termination Rule │ │ 线程的所有操作 HB 其他线程检测到该线程终止 │ │ │ │ 6. 传递性规则Transitivity │ │ A HB BB HB C ⇒ A HB C │ │ │ └──────────────────────────────────────────────────────────────────┘3.3 实战应用// Happens-Before 应用示例// 场景双重检查锁定单例publicclassSingleton{privatestaticvolatileSingletoninstance;publicstaticSingletongetInstance(){if(instancenull){// T1: 读取 instancesynchronized(Singleton.class){if(instancenull){// T2: 再次检查instancenewSingleton();// 分解为三步// 1. 分配内存// 2. 调用构造函数// 3. 写入 instance 引用// volatile 保证这 3 步不会重排序}}}returninstance;}}四、内存屏障4.1 内存屏障类型┌──────────────────────────────────────────────────────────────────┐ │ 内存屏障类型 │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ LoadLoad 屏障 │ │ 确保 Load1 在后续 Load2 之前读取数据 │ │ ───────────────────────────── │ │ │ │ StoreStore 屏障 │ │ 确保 Store1 在后续 Store2 之前刷新到主内存 │ │ ───────────────────────────── │ │ │ │ LoadStore 屏障 │ │ 确保 Load 在后续 Store 之前完成 │ │ ───────────────────────────── │ │ │ │ StoreLoad 屏障 │ │ 确保 Store 在后续 Load 之前完成 │ │ 最重的一种屏障同时保证可见性和有序性 │ │ │ └──────────────────────────────────────────────────────────────────┘4.2 volatile 的底层实现// volatile 写操作的汇编代码// 对 volatile 变量的写入会生成内存屏障指令// javap -c -v 输出publicvoidsetValue(int);0:aload_01:iload_12:putfield #2// putfield 本身包含 StoreStore 屏障5:return// volatile 读操作的汇编代码publicintgetValue();0:aload_01:getfield #2// getfield 本身包含 LoadLoad LoadStore 屏障4:ireturn五、总结JMM 是 Java 并发编程的基石。通过 Happens-Before 规则和内存屏障JMM 解决了多线程环境下的原子性、可见性、有序性问题。理解 JMM才能理解 volatile、synchronized、final 等关键字的底层原理写出正确的多线程代码。系列导航上一篇【JVM深度解析】第23篇字节码执行引擎深度剖析下一篇【JVM深度解析】第25篇volatile与synchronized深度原理系列目录JVM深度解析参考资料JSR 133: Java Memory ModelJava Memory Model - Oracle DocumentationUnderstanding JMM - Jakob Jenkov

相关文章:

【JVM深度解析】第24篇:JVM内存模型(JMM)核心原理

摘要 JMM(Java Memory Model,Java 内存模型)是 Java 并发编程的基础,它定义了线程之间共享变量的可见性、有序性问题,以及如何通过 Happens-Before 规则和内存屏障来解决这些问题。理解 JMM,你才能真正明白…...

Hive Lateral View + posexplode 实战:从数据炸裂到业务洞察

1. 从爆炸到洞察:为什么需要posexplode? 刚接触Hive时,我和大多数人一样先学会了explode函数。它能轻松把数组炸开成多行,处理JSON数据特别顺手。但直到遇到一个用户行为分析的需求,我才发现explode有个致命缺陷——它…...

水性浸涂漆工艺规范:从调配到干燥,讲透五金浸涂所有细节

在水性工业漆的实际应用中,浸涂工艺因其效率高、适合大批量小五金件(如螺栓、垫圈、弹簧、小型电机壳、刹车钳、千斤顶零部件等)而备受青睐。但很多工厂在浸漆时常常遇到气泡、流挂、膜厚不均等问题。本文以敦普水性工业漆的水性浸涂漆为例&a…...

Obsidian Dataview数据索引与查询引擎:构建智能知识库的完整技术方案

Obsidian Dataview数据索引与查询引擎:构建智能知识库的完整技术方案 【免费下载链接】obsidian-dataview A data index and query language over Markdown files, for https://obsidian.md/. 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian-dataview …...

把MobileMamba的‘多感受野’模块拆给你看:如何用WTE-Mamba和MK-DeConv给老模型做一次‘微创手术’

MobileMamba模块化改造实战:如何用WTE-Mamba和MK-DeConv升级传统视觉模型 当你在深夜调试一个基于ResNet的图像分类项目时,是否遇到过这样的困境——模型在局部细节识别上表现尚可,但面对需要全局上下文理解的场景时总是力不从心?…...

[RV1109/RV1126实战]-RGA与DRM协同优化:从零构建图像Resize加速引擎

1. 为什么需要RGA与DRM协同优化图像Resize? 在嵌入式视觉开发中,图像缩放(Resize)是最基础也是最耗时的操作之一。我在RV1126平台上实测发现,用OpenCV的resize函数处理一张640x480的RGB图像需要22ms,而同样…...

基于Docker与WebVirtCloud的私有云实践:从零部署到虚拟机管理

1. 为什么选择DockerWebVirtCloud搭建私有云 最近几年我帮不少中小企业部署过私有云环境,发现很多团队都被传统虚拟化方案的复杂部署流程劝退。直到遇到WebVirtCloud这个基于Web的KVM管理工具,配合Docker容器化部署,真正实现了十分钟快速搭建…...

无名杀:免费开源的三国杀网页游戏终极体验指南

无名杀:免费开源的三国杀网页游戏终极体验指南 【免费下载链接】noname 项目地址: https://gitcode.com/GitHub_Trending/no/noname 无名杀是一款完全免费、开源的网页版三国杀游戏,将经典的三国杀玩法与现代Web技术完美结合。这款游戏不仅忠实还…...

保姆级教程:在ROS2 Humble上为TurtleBot4仿真环境手动编译Cartographer(含源码修改输出轨迹)

从零构建ROS2 Humble下的Cartographer:TurtleBot4仿真环境深度定制指南 在机器人领域,实时定位与地图构建(SLAM)一直是核心技术难题。对于使用TurtleBot4进行研究的开发者而言,官方提供的Cartographer二进制包往往无法满足特定需求&#xff0…...

老旧Mac网络重生:OpenCore Legacy Patcher的无线修复方案

老旧Mac网络重生:OpenCore Legacy Patcher的无线修复方案 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 当2007-2017年间的Mac设备升级到新版mac…...

SITS2026案例深度拆解:为什么同一Prompt在Kubernetes集群A生成合规代码,在集群B触发安全熔断?(附YAML级差异比对表)

第一章:SITS2026案例:AI云原生代码生成 2026奇点智能技术大会(https://ml-summit.org) SITS2026(Smart Intelligent Transformation Suite 2026)是面向金融核心系统的云原生AI工程实践平台,其核心能力之一是基于多模…...

模型越强,检测越假?深度剖析Transformer嵌入空间下的语义克隆盲区,及3步可落地的对抗校验法

第一章:模型越强,检测越假?深度剖析Transformer嵌入空间下的语义克隆盲区,及3步可落地的对抗校验法 2026奇点智能技术大会(https://ml-summit.org) 当LLM生成文本在语义层面无限趋近人类表达时,基于余弦相似度或BERT…...

从《黑神话:悟空》到独立游戏:聊聊Avatar肌肉设置如何塑造角色个性走姿

从《黑神话:悟空》到独立游戏:如何用Avatar肌肉参数打造角色灵魂步态 在《黑神话:悟空》的实机演示中,主角一个转身抖落披风的动作让全网沸腾——这不仅是美术的胜利,更是动画系统的精妙设计。当大多数独立游戏还在使用…...

告别单调界面:用ttkbootstrap为你的Python GUI注入现代美学

1. 为什么你的Python GUI需要ttkbootstrap? 如果你用过Python自带的tkinter库开发图形界面,大概率会对它默认的"复古风格"印象深刻——灰底蓝框的按钮、朴素的输入框、毫无设计感的布局,活脱脱像是从Windows 98穿越过来的程序。我去…...

OpenClaw 这样卸载才够干净,全程 5 大步

大家好,这里是小凡 AI 研习社,我是小凡。 之前在《安装教程》和《安装教程补充版》中,我们详细讲解了 OpenClaw 的安装流程,本节课就来完整介绍它的卸载方法。 一、哪些地方有 OpenClaw 的相关内容? OpenClaw 要想卸…...

告别`sudo gem install`失败:用Homebrew在Mac上无痛管理多版本Ruby环境

告别sudo gem install失败:用Homebrew在Mac上无痛管理多版本Ruby环境 每次在Mac上安装Cocoapods时遇到sudo gem install报错,是不是让你抓狂?系统权限问题、Ruby版本冲突、网络连接超时——这些坑我全都踩过。今天分享的这套方法,…...

我的编程成长日记|双非一本通信大三生的破局之路✨

大家好!这是我在技术路上的第一篇博客,作为一名双非一本院校的通信工程大三学生,我想在这里记录自己从通信转码、拥抱编程的起点,也立好未来的成长flag。一、关于我我是一名就读于双非一本院校的通信工程大三学生,目前…...

告别鼠标!用AutoHotKey一键搞定音量调节(附开机自启设置)

解放双手:用AutoHotKey打造专业级音量控制方案 在视频剪辑、远程会议或深夜观影时,频繁伸手去够物理音量键不仅打断工作流,还影响沉浸感。AutoHotKey(AHK)作为Windows平台的自动化神器,能让我们用键盘组合键…...

微信小程序Canvas实战:5分钟实现图片自由拖拽+缩放旋转(附完整代码)

微信小程序Canvas进阶:打造高互动性图片编辑器 在移动互联网时代,图片编辑已成为社交分享的刚需功能。微信小程序凭借其轻量级特性,结合Canvas的强大绘图能力,为开发者提供了实现复杂图片交互的可能。本文将带你从零构建一个支持拖…...

【踩坑实录】前端开发必看:一次由CSS缓存引发的线上事故与SEO反思

各位老铁,今天不聊虚的,来复盘一下我上周五晚上亲手制造的一场“线上事故”。作为一名前端开发,我一直以为接入CDN就是改个CNAME那么简单,直到我用实际行动证明了:不懂缓存策略,就是在给线上环境埋雷。一、…...

一文了解医疗废水处理行业!

相信大家都明白,在医院这类复杂的场所,排放的废水肯定也很复杂,其中是会包含各种有毒、有害的物理化学以及反射性的污染等,还存在空间性、急性等特征。接下来我们一文了解什么是医疗废水处理行业!其实医疗废水处理行业…...

发现一款超好用的 Markdown 一键排版工具

作为一名经常写技术文章的博主,排版一直是让我头疼的问题。最近发现了一款在线排版工具,用了一段时间后觉得非常不错,分享给大家! 一、为什么需要排版工具? 在内容创作时代,优质内容是王道,而精…...

从分子结构到智能药物发现:RDKit化学信息学实战指南

从分子结构到智能药物发现:RDKit化学信息学实战指南 【免费下载链接】rdkit The official sources for the RDKit library 项目地址: https://gitcode.com/gh_mirrors/rd/rdkit 化学信息学正在彻底改变药物研发的范式,而RDKit作为这一领域的瑞士军…...

3000+科研图标免费下载:Bioicons如何让科学可视化变得简单?

3000科研图标免费下载:Bioicons如何让科学可视化变得简单? 【免费下载链接】bioicons A library of free open source icons for science illustrations in biology and chemistry 项目地址: https://gitcode.com/gh_mirrors/bi/bioicons 还在为科…...

大麦网自动抢票脚本:3分钟快速部署,轻松应对热门演唱会秒杀

大麦网自动抢票脚本:3分钟快速部署,轻松应对热门演唱会秒杀 【免费下载链接】Automatic_ticket_purchase 大麦网抢票脚本 项目地址: https://gitcode.com/GitHub_Trending/au/Automatic_ticket_purchase 还在为抢不到热门演唱会门票而烦恼吗&…...

数据并行训练深度解析:为什么梯度要取平均?

数据并行训练深度解析:为什么梯度要取平均? 一、引言 在大模型训练时代,单张GPU已经无法满足训练需求。数据并行(Data Parallelism)是最常用、最直观的分布式训练策略。但很多初学者会有一个疑问:梯度同步时…...

告别Vysor!用Scrcpy在Mac上无线投屏安卓手机(附魅族16th闪退修复实战)

开源投屏神器Scrcpy在Mac上的终极配置指南 在数字工作流中,安卓设备与电脑的无缝协作已成为刚需。商业投屏工具虽然方便,但往往伴随着高昂订阅费、性能瓶颈和隐私顾虑。Scrcpy作为一款开源解决方案,不仅完全免费,更以接近零延迟的…...

7个实战技巧:用ILSpyCmd高效处理企业级.NET程序集反编译

7个实战技巧:用ILSpyCmd高效处理企业级.NET程序集反编译 【免费下载链接】ILSpy .NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform! 项目地址: https://gitcode.com/gh_mirrors/il/ILSpy 在当今的.NET开…...

知识抽取避坑手册:关系抽取中90%人会犯的3个标注错误(附真实案例)

知识抽取避坑手册:关系抽取中90%人会犯的3个标注错误(附真实案例) 在电商平台的商品评论中,当用户评价"这款手机充电速度和官方描述一致"时,新手标注员常会忽略"充电速度"与"官方描述"之…...

从配置文件到配置类:Spring Boot Security 的权限控制演进

1. Spring Security 的配置文件时代 记得我第一次用 Spring Security 是在五年前的一个内部管理系统项目上。当时为了快速上线,直接在 application.yml 里写死了用户名密码,就像这样: spring:security:user:name: adminpassword: 123456roles…...