面试官:你了解Java内存模型JMM吗?

面试官

哈喽,大家好🎉,我是世杰

欢迎大家关注我的公众号『程序员世杰』获取更多面试内容🎉🎉!

本文我为大家介绍面试官经常考察的「Java内存模型JMM相关内容

面试连环call

  1. 什么是Java内存模型(JMM)? 为什么需要JMM?
  2. Java线程的工作内存和主内存各自的作用?
  3. Java缓存一致性问题?
  4. Java的并发编程问题?

要想理解透彻 JMM(Java 内存模型),我们先要从 『硬件内存结构』 说起。让我们开始吧!🎉🎉🎉

1. 硬件内存结构

1.1 CPU 缓存模型

img

(1)CPU Register

CPU Register 也就是 CPU 寄存器。CPU 寄存器是 CPU 内部集成的,在寄存器上执行操作的效率要比在主存上高出几个数量级。

(2)CPU Cache Memory

CPU Cache Memory 也就是 CPU 高速缓存,相对于寄存器来说,通常也可以成为 L2 二级缓存。相对于硬盘读取速度来说内存读取的效率非常高,但是与 CPU 还是相差数量级,所以在 CPU 和主存间引入了多级缓存。CPU 缓存则是为了解决 CPU 处理速度和内存处理速度不对等的问题。

(3)Main Memory

Main Memory 就是主存,主存比 L1、L2 缓存要大很多。

1.2 缓存一致性问题

由于主存与 CPU 处理器的运算能力之间有数量级的差距,所以在传统计算机内存架构中会引入高速缓存来作为主存和处理器之间的缓冲。先复制一份数据到 CPU Cache 中,当 CPU 需要用到的时候就可以直接从 CPU Cache 中读取数据,当运算完成后,再将运算得到的数据写回 Main Memory 中。

但是,这样存在 内存缓存不一致性的问题 !比如我执行一个 i++ 操作的话,如果两个线程同时执行的话,假设两个线程从 CPU Cache 中读取的 i=1,两个线程做了 i++ 运算完之后再写回 Main Memory 之后 i=2,而正确结果应该是 i=3。

CPU 为了解决内存缓存不一致性问题可以通过**制定缓存一致协议(比如 [MESI 协议open in new window])**或者其他手段来解决。 这个缓存一致性协议指的是在 CPU 高速缓存与主内存交互的时候需要遵守的原则和规范。不同的 CPU 中,使用的缓存一致性协议通常也会有所不同。

img

1.3 指令重排序

什么是指令重排序? 简单来说就是系统在执行代码的时候并不一定是按照你写的代码的顺序依次执行。

常见的指令重排序有下面 2 种情况:

  • 编译器优化重排:编译器(包括 JVM、JIT 编译器等)在不改变单线程程序语义的前提下,重新安排语句的执行顺序。
  • 指令并行重排:现代处理器采用了指令级并行技术(Instruction-Level Parallelism,ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
  • 内存系统的重排序:由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

img

Java 源代码会经历 编译器优化重排 —> 指令并行重排 —> 内存系统重排 的过程,最终才变成操作系统可执行的指令序列。

2. Java内存模型

2.1 Java内存与硬件内存

之前讲 JVM 运行时内存区域时,聊到 JVM 分为栈、堆等,这些都是 JVM 定义的概念。在传统的硬件内存架构中是没有栈和堆这种概念。从图中可以看出栈和堆既存在于高速缓存中又存在于主内存中,所以Java内存和硬件内存没有直接的关系。

img

2.2 Java内存与主内存

(1) Java 内存模型规定所有的变量都存储在主内存中,每条线程都有自己的工作内存。

(2) 线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。

(3) 不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递都需要通过主内存来完成。

jmm

  • 主内存:所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量,还是局部变量类信息常量静态变量都是放在主内存中。为了获取更好的运行速度,虚拟机及硬件系统可能会让工作内存优先存储于寄存器和高速缓存中。

  • 本地内存:每个线程都有一个私有的本地内存,本地内存存储了该线程以读 / 写共享变量的副本。每个线程只能操作自己本地内存中的变量,无法直接访问其他线程的本地内存。如果线程间需要通信,必须通过主内存来进行。本地内存是 JMM 抽象出来的一个概念,并不真实存在,它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。

为了更好的控制主内存和本地内存的交互,Java 内存模型定义了八种操作来实现:

『主内存』

  • lock:锁定。作用于主内存的变量,把一个变量标识为一条线程独占状态

  • unlock:解锁。作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

  • read:读取。作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用

  • write:写入。作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中

『工作内存』

  • load:载入。作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中

  • use:使用。作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作

  • assign:赋值。作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作

  • store:存储。作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作

3. 再聊并发编程

熟悉 Java 并发编程的同学肯定对这三个问题很熟悉:『可见性问题』『原子性问题』『有序性问题』

3.1 有序性

由于指令重排序问题,代码的执行顺序未必就是编写代码时候的顺序。我们上面讲重排序的时候也提到过:指令重排序可以保证串行语义一致,但是没有义务保证多线程间的语义也一致 ,所以在多线程下,指令重排序可能会导致一些问题。

在 Java 中,volatile 关键字可以禁止指令进行重排序优化。

3.2 原子性

一次操作或者多次操作,要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断,要么都不执行

在 Java 中,可以借助synchronized、各种 Lock 以及各种原子类实现原子性。

3.3 可见性

当一个线程对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。

在 Java 中,可以借助synchronizedvolatile 以及各种 Lock 实现可见性。

参考文章

  1. Java 内存模型引入

  2. ****************

  3. 说说什么是Java内存模型?

  4. ************************

#Java##面试#
全部评论

相关推荐

03-23 18:31
已编辑
中国农业大学 Java
Q: Redis 去重方案,是使用“用户 ID + 消息模板 ID”作为键吗?这里的模板 ID是指每个配置的模板类型拥有唯一 ID,还是说不同配置下 ID 会重复?Q:项目中使用的 Redis 架构模式是什么?是单节点、哨兵模式(Sentinel)还是集群模式(Cluster)Q:引入 Apollo 配置中心是为了解决什么问题Q: Kafka 部署时如何搭建生产环境和消费环境?具体的集群架构或部署方式是?Q:关于渠道业务,虽然目前数据量不大可能不需要限流,后续流量增大,如何设计限流策略Q:项目中的线程池具体用哪种方式创建或配置(例如通过 ThreadPoolExecutor 构造函数还是工具类)?Q:详细描述项目中使用线程池的整体逻辑流程,包括任务提交、执行以及后续处理的步骤Q:线程池的拒绝策略主要有哪几种Q:核心线程数(corePoolSize)在运行过程中是否可以动态减少Q:数据清洗过程中是否考虑过可能出现消息乱序或数据重复的问题?如果有,如何解决Q:使用的 MySQL 数据库中,单表的最大数据量大概达到多少Q:在数据库中添加索引的主要目的是什么Q:添加索引时,特定的规范或原则?具体的优化策略?Q:简要解释回表Q:对于一条包含 SELECT * FROM table WHERE condition ORDER BY ... LIMIT ... 的 SQL 语句,各个子句的执行顺序Q:如果是多表关联查询(涉及主表和子表),MySQL 底层是如何处理和执行这些连接操作的?Q:假设开发的一个接口响应时间长达一分钟,需要从哪些方面入手进行优化?简述优化思路。Q:结合您的开发经历,你认为引入大模型技术相比传统开发方式,主要在哪些方面提升了开发效率或系统能力Q:多场景对话支持中,系统如何实现不同场景之间的隔离与共享Q:主要使用了什么存储介质来保存对话的状态、记忆或中间数据
点赞 评论 收藏
分享
03-09 15:23
已编辑
蚌埠坦克学院 前端工程师
由于主包的拖延症,一直拖到了现在才写这篇帖子,因为觉得自己的去向一般,迟迟也没有动笔,突然在一个肝论文的深夜,看到自己写的暑期实习的帖子,决定还是提笔记录一下这段难忘的时光主包于9月初结束了在某互联网大厂的暑期实习,并没有成功转正其实在7月初就看出了端倪,并在8月上旬就开始复习并准备秋招,从8月中旬就猛猛加入面试大军了但是真的要提的一点是:传统开发已经不太推荐走了,不管是传统的前端还是后端,目前AI已成潮流,无论什么岗位都在问AI相关的知识,同时AI使得开发岗的工作效率大幅提高,一个人能干好几个人的活儿根据今年秋招的形式来看,不沾AI的传统开发岗位基本工资都开不高,而沾AI的AI infra、大模型算法,以及与大模型业务有关的应用开发,基础上下游业务不管是岗位数还是薪资比起往年都涨了很大一截,因此推荐后浪宝宝们(特别是学历里带9的),勇敢的加入ai的浪潮吧!互联网的offer如下:(1)京东:二轮面试+一轮HR,开奖大SP(2)Shopee:三轮面试+一轮HR,开奖大SP(3)钉钉:二轮面试+一轮HR,大白菜(4)高德:二轮面试+一轮HR,小SP(阿里今年给传统开发是真的开不起价)(5)阿里云:二轮面试+一轮HR,面试均通过,泡死了(很有可能是暑期拒过offer的原因)(6)滴滴:三轮面试+无HR,大SP(7)得物:两轮还是三轮我忘了,面试都过了,12月跑过来和我说原部门没有HC了让我换部门加面,寻思不会去,拒面了,就没开奖(8)字节系懂车帝:三轮面试+一轮HR,感觉已经给到他们那边的顶了,应该是SSP了(9)字节跳动:三轮面试+一轮HR,开奖大SP其余流程:小红书二面拒面OPPO三面拒面百度泡挂了阿里国际泡挂了小鹏HR面拒面值得一提的是腾讯,先是WXG捞我起来,不能从10月实习到毕业秒挂,接着又是CDG,面到三面了已经说到进来做什么了,秒挂,被IEG捞起来KPI面,面试官急的像要去投胎,然后又被WXG捞起来二面过,不约三面直接挂,腾讯你是要咋的?(怨气值MAX)在互联网结束后,又加入了国央企的面试行列,事实证明,即使双九+互联网实习,在目前国央企行列中依然啥也不是因为我主要投电网、银行总行+省公司、以及中证登、中金所、中债登、运营商等单位,根据今年观察到的情况,参加面试的大部分都是双九或2本9硕,且人手大厂实习,竞争可以说是非常的激烈,也许稳定对于这个高速变化的时代,可能真有一丝吸引力吧。。。我并不是一个很高精力的人,很容易累,抗压能力不好,在面试时候向面试官展示的喜欢新技术,其实不然,完全是一个不太喜欢写代码的人。。。在整个国央企求职周期中(10月-12月),主包在往返北京,上海,杭州,南京,广州等城市多次,交通和住宿合计花了一万元+的情况下一共就拿到两个offer,其中一个就是主包最终的去向(怕开盒就不说是哪家了)PS. 是的,主包在经过的反复地思考,权衡之后,最终还是选择了国企,根据大模型发展的速度,非常担心传统前后端开发,尤其是业务未来的行情,最终还是选择跑路国企了,也不知道这选择是否正确,但是确实是觉得传统前端开发很难吃到AI红利,还容易被取代,前途不甚光明了。当时也曾犹豫过,两边似乎都有自己想要的东西,羡慕互联网的高薪和自由,同时厌恶他的高压,担心传统前端开发未来的前景。选择国央企,其实也失去了很多东西,包括自由,高薪,看到身边的同学一毕业在互联网拿自己4-5倍的工资,以求得在时代浪潮中一时的稳定(未来稳不稳定也不好说,轻松也不一定轻松)归结到最后还是一个问题:你究竟想要什么样的生活?这篇帖子留给牛客社区的后浪们,希望大家都能在下一年的秋招中拿大包!也留给我自己,致敬我自己艰难且并不算太成功的求职之路,始于研二秋,终于研三冬,历经一年半载,付出无数日夜,八股文、算法题、项目背到厌烦。整个人在思想上也发生了很大的蜕变,是真正意义上的成长了。希望我工作后,回来看到这篇帖子时,也能够像现在的我看到暑期实习结束时的帖子时一样,还愿意动笔,写一点什么吧。“潺潺流水终于翻过了群山一座座。”碎碎念(主包失败的感情经历 and 秋招结束后的日本旅游之旅):在签完三方之后,和喜欢的师兄一起(没错,接前文,是暑期实习那会去哈尔滨找的那个)去了日本北海道和东京旅游,见到了完全不一样的世界,人生第一次出国,玩的特别特别开心,虽然不知道和我同行的他究竟玩的开不开心。我们12月下旬离开大陆,前往北海道,看过了小樽的雪,一起在《情书》的船见坂打卡,看小樽的海,在小樽邮局猛猛拍照,一起逛过了札幌的街道,在狸小路商店街疯狂shopping,在白色恋人公园打卡,也逛过北海道神宫和北海道大学,小傻瓜还能买错机票导致损失5000块。。。我聪明无比的师兄怎么会犯这种错误呢,但大部分时候还是觉得他好可爱喔(恋爱的酸臭味,好像我真的很久没有这么喜欢过一个人了)我们在25年的最后一天,看过了富士山,我常听陈奕迅的富士山下,不曾想他有一天真的能实现。即使“谁能凭爱意要富士山私有。”在26年的第一天起个大早,一起来到浅草寺祈福,去了心心念念的秋叶原(二次元圣地了属于是)也看了东京塔的夜景,去了传说的歌舞伎町。1月2日东京下了一场大雪,我站在雪里想,如果我们能一起看到该多好。后来我独自走了很多地方,去了涉谷,银座,逛了一直想去却没机会的梦校——东京大学。离开东京那天,一个人在日暮里散步,空荡的街道上,时间似乎也变得很慢,一切回归平静,我真的非常喜欢这样的生活。在日本的旅途就像一场永远也不会醒来的梦,虽然是听不懂的语言,不熟悉的交通,但是一切都充满新鲜感,让我感受到——我是真真切切的在“活着”。回国之后,在很多个瞬间,我总是会回忆起二人在日本的时光,原来思念是这样难熬的东西,它不会轰轰烈烈地出现,只会在某个夜晚、某个街角、某个熟悉的瞬间,突然把人拉回过去。我把浅草寺的签裱进了相框里,像是把那段时间小心地封存起来。我想留下些什么,证明我们真的曾经一起走过那段路。我无法留住离开的人,但回忆我会永远留在心底。原来有些相遇,真的不是为了一个结局,而是为了让我们在彼此的生命里留下些什么。“忘掉我跟你恩怨 樱花开了几转 东京之旅一早比一世遥远。”——谢谢你,再见了。
风起天阑_:我就说我们工科生是有文笔的
26届求职交流
点赞 评论 收藏
分享
评论
5
8
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务