面試官:為什麼需要Java內存模型?

Java3y 2021-10-14 09:05:42
需要 java 模型


面試官今天想跟你聊聊Java內存模型,這塊你了解過嗎?

候選者:嗯,我簡單說下我的理解吧。那我就從為什麼要有Java內存模型開始講起吧

面試官:開始你的錶演吧。

候選者:那我先說下背景吧

候選者:1. 現有計算機往往是多核的,每個核心下會有高速緩存。高速緩存的誕生是由於「CPU與內存(主存)的速度存在差异」,L1和L2緩存一般是「每個核心獨占」一份的。

候選者:2. 為了讓CPU提高運算效率,處理器可能會對輸入的代碼進行「亂序執行」,也就是所謂的「指令重排序」

候選者:3. 一次對數值的修改操作往往是非原子性的(比如i++實際上在計算機執行時就會分成多個指令)

候選者:在永遠單線程下,上面所講的均不會存在什麼問題,因為單線程意味著無並發。並且在單線程下,編譯器/runtime/處理器都必須遵守as-if-serial語義,遵守as-if-serial意味著它們不會對「數據依賴關系的操作」做重排序。

候選者:CPU為了效率,有了高速緩存、有了指令重排序等等,整塊架構都變得複雜了。我們寫的程序肯定也想要「充分」利用CPU的資源啊!於是乎,我們使用起了多線程

候選者:多線程在意味著並發,並發就意味著我們需要考慮線程安全問題

候選者:1. 緩存數據不一致:多個線程同時修改「共享變量」,CPU核心下的高速緩存是「不共享」的,那多個cache與內存之間的數據同步該怎麼做?

候選者:2. CPU指令重排序在多線程下會導致代碼在非預期下執行,最終會導致結果存在錯誤的情况。

候選者:針對於「緩存不一致」問題,CPU也有其解决辦法,常被大家所認識的有兩種:

候選者:1.使用「總線鎖」:某個核心在修改數據的過程中,其他核心均無法修改內存中的數據。(類似於獨占內存的概念,只要有CPU在修改,那別的CPU就得等待當前CPU釋放)

候選者:2.緩存一致性協議(MESI協議,其實協議有很多,只是舉個大家都可能見過的)。MESI拆開英文是(Modified (修改狀態)、Exclusive (獨占狀態)、Share(共享狀態)、Invalid(無效狀態))

候選者:緩存一致性協議我認為可以理解為「緩存鎖」,它針對的是「緩存行」(Cache line) 進行"加鎖",所謂「緩存行」其實就是 高速緩存 存儲的最小單比特。

面試官:嗯...

候選者:MESI協議的原理大概就是:當每個CPU讀取共享變量之前,會先識別數據的「對象狀態」(是修改、還是共享、還是獨占、還是無效)。

候選者:如果是獨占,說明當前CPU將要得到的變量數據是最新的,沒有被其他CPU所同時讀取

候選者:如果是共享,說明當前CPU將要得到的變量數據還是最新的,有其他的CPU在同時讀取,但還沒被修改

候選者:如果是修改,說明當前CPU正在修改該變量的值,同時會向其他CPU發送該數據狀態為invalid(無效)的通知,得到其他CPU響應後(其他CPU將數據狀態從共享(share)變成invalid(無效)),會當前CPU將高速緩存的數據寫到主存,並把自己的狀態從modify(修改)變成exclusive(獨占)

候選者:如果是無效,說明當前數據是被改過了,需要從主存重新讀取最新的數據。

候選者:其實MESI協議做的就是判斷「對象狀態」,根據「對象狀態」做不同的策略。關鍵就在於某個CPU在對數據進行修改時,需要「同步」通知其他CPU,錶示這個數據被我修改了,你們不能用了。

候選者:比較於「總線鎖」,MESI協議的"鎖粒度"更小了,性能那肯定會更高咯

面試官但據我了解,CPU還有優化,你還知道嗎?

候選者:嗯,還是了解那麼一點點的。

候選者:從前面講到的,可以發現的是:當CPU修改數據時,需要「同步」告訴其他的CPU,等待其他CPU響應接收到invalid(無效)後,它才能將高速緩存數據寫到主存。

候選者:同步,意味著等待,等待意味著什麼都幹不了。CPU肯定不樂意啊,所以又優化了一把。

候選者:優化思路就是從「同步」變成「异步」。

候選者:在修改時會「同步」告訴其他CPU,而現在則把最新修改的值寫到「store buffer」中,並通知其他CPU記得要改狀態,隨後CPU就直接返回幹其他事了。等到收到其它CPU發過來的響應消息,再將數據更新到高速緩存中。

候選者:其他CPU接收到invalid(無效)通知時,也會把接收到的消息放入「invalid queue」中,只要寫到「invalid queue」就會直接返回告訴修改數據的CPU已經將狀態置為「invalid」

候選者:而异步又會帶來新問題:那我現在CPU修改完A值,寫到「store buffer」了,CPU就可以幹其他事了。那如果該CPU又接收指令需要修改A值,但上一次修改的值還在「store buffer」中呢,沒修改至高速緩存呢。

候選者:所以CPU在讀取的時候,需要去「store buffer」看看存不存在,存在則直接取,不存在才讀主存的數據。【Store Forwarding】

候選者:好了,解决掉第一個异步帶來的問題了。(相同的核心對數據進行讀寫,由於异步,很可能會導致第二次讀取的還是舊值,所以首先讀「store buffer」。

面試官還有其他?

候選者:那當然啊,那「异步化」會導致相同核心讀寫共享變量有問題,那當然也會導致「不同」核心讀寫共享變量有問題啊

候選者:CPU1修改了A值,已把修改後值寫到「store buffer」並通知CPU2對該值進行invalid(無效)操作,而CPU2可能還沒收到invalid(無效)通知,就去做了其他的操作,導致CPU2讀到的還是舊值。

候選者:即便CPU2收到了invalid(無效)通知,但CPU1的值還沒寫到主存,那CPU2再次向主存讀取的時候,還是舊值...

候選者:變量之間很多時候是具有「相關性」(a=1;b=0;b=a),這對於CPU又是無感知的...

候選者:總體而言,由於CPU對「緩存一致性協議」進行的异步優化「store buffer」「invalid queue」,很可能導致後面的指令很可能查不到前面指令的執行結果(各個指令的執行順序非代碼執行順序),這種現象很多時候被稱作「CPU亂序執行」

候選者:為了解决亂序問題(也可以理解為可見性問題,修改完沒有及時同步到其他的CPU),又引出了「內存屏障」的概念。

面試官:嗯...

候選者:「內存屏障」其實就是為了解决「异步優化」導致「CPU亂序執行」/「緩存不及時可見」的問題,那怎麼解决的呢?嗯,就是把「异步優化」給”禁用“掉(:

候選者:內存屏障可以分為三種類型:寫屏障,讀屏障以及全能屏障(包含了讀寫屏障),屏障可以簡單理解為:在操作數據的時候,往數據插入一條"特殊的指令"。只要遇到這條指令,那前面的操作都得「完成」。

候選者:那寫屏障就可以這樣理解:CPU當發現寫屏障的指令時,會把該指令「之前」存在於「store Buffer」所有寫指令刷入高速緩存。

候選者:通過這種方式就可以讓CPU修改的數據可以馬上暴露給其他CPU,達到「寫操作」可見性的效果。

候選者:那讀屏障也是類似的:CPU當發現讀屏障的指令時,會把該指令「之前」存在於「invalid queue」所有的指令都處理掉

候選者:通過這種方式就可以確保當前CPU的緩存狀態是准確的,達到「讀操作」一定是讀取最新的效果。

候選者:由於不同CPU架構的緩存體系不一樣、緩存一致性協議不一樣、重排序的策略不一樣、所提供的內存屏障指令也有差异,為了簡化Java開發人員的工作。Java封裝了一套規範,這套規範就是「Java內存模型」

候選者:再詳細地說,「Java內存模型」希望 屏蔽各種硬件和操作系統的訪問差异,保證了Java程序在各種平臺下對內存的訪問都能得到一致效果。目的是解决多線程存在的原子性、可見性(緩存一致性)以及有序性問題。

面試官那要不簡單聊聊Java內存模型的規範和內容吧?

候選者:不了,怕一聊就是一個下午,下次吧?

本文總結

  • 並發問題產生的三大根源是「可見性」「有序性」「原子性」
  • 可見性:CPU架構下存在高速緩存,每個核心下的L1/L2高速緩存不共享(不可見)
  • 有序性:主要有三方面可能導致打破
    • 編譯器優化導致重排序(編譯器可以在不改變單線程程序語義的情况下,可以對代碼語句順序進行調整重新排序)
    • 指令集並行重排序(CPU原生就有可能將指令進行重排)
    • 內存系統重排序(CPU架構下很可能有store buffer /invalid queue 緩沖區,這種「异步」很可能會導致指令重排)
  • 原子性:Java的一條語句往往需要多條 CPU 指令完成(i++),由於操作系統的線程切換很可能導致 i++ 操作未完成,其他線程“中途”操作了共享變量 i ,導致最終結果並非我們所期待的。
  • 在CPU層級下,為了解决「緩存一致性」問題,有相關的“鎖”來保證,比如“總線鎖”和“緩存鎖”。
    • 總線鎖是鎖總線,對共享變量的修改在相同的時刻只允許一個CPU操作。
    • 緩存鎖是鎖緩存行(cache line),其中比較出名的是MESI協議,對緩存行標記狀態,通過“同步通知”的方式,來實現(緩存行)數據的可見性和有序性
    • 但“同步通知”會影響性能,所以會有內存緩沖區(store buffer/invalid queue)來實現「异步」進而提高CPU的工作效率
    • 引入了內存緩沖區後,又會存在「可見性」和「有序性」的問題,平日大多數情况下是可以享受「异步」帶來的好處的,但少數情况下,需要强「可見性」和「有序性」,只能"禁用"緩存的優化。
    • “禁用”緩存優化在CPU層面下有「內存屏障」,讀屏障/寫屏障/全能屏障,本質上是插入一條"屏障指令",使得緩沖區(store buffer/invalid queue)在屏障指令之前的操作均已被處理,進而達到 讀寫 在CPU層面上是可見和有序的。
  • 不同的CPU實現的架構和優化均不一樣,Java為了屏蔽硬件和操作系統訪問內存的各種差异,提出了「Java內存模型」的規範,保證了Java程序在各種平臺下對內存的訪問都能得到一致效果

歡迎關注我的微信公眾號【Java3y】來聊聊Java面試,對線面試官系列持續更新中!

【對線面試官-移動端】系列 一周兩篇持續更新中!

【對線面試官-電腦端】系列 一周兩篇持續更新中!

原創不易!!求三連!!

版权声明
本文为[Java3y]所创,转载请带上原文链接,感谢
https://javamana.com/2021/10/20211014090341137d.html

  1. Usage relations and differences of count (1), count (*) and count (a field) in MySQL
  2. 2021 Ali Java advanced interview questions sharing, Java Architect interview materials
  3. Mybatis - dynamic SQL statement - if usage - MySQL series learning notes
  4. [go to Dachang series] deeply understand the use of where 1 = 1 in MySQL
  5. [secret room escape game theme ranking list] Based on spring MVC + Spring + mybatis
  6. Redis log: the killer mace of fearless downtime and rapid recovery
  7. 5 minutes to build redis cluster mode and sentinel mode with docker
  8. Java小白入门200例106之遍历ArrayList的几种方式
  9. Java小白入门200例105之Java ArrayList类
  10. Java小白入门200例104之JDK自带记录日志类logging
  11. Practice of high availability architecture of Tongcheng travel network based on rocketmq
  12. Chapter 9 - Linux learning will - file archiving and compression tar --- zip
  13. Java小白入門200例104之JDK自帶記錄日志類logging
  14. JDK avec journalisation de classe dans 200 cas 104
  15. Java ArrayList Class for Introduction to Java LITTLE WHITE 200 example 105
  16. Plusieurs façons de traverser ArrayList à partir de 200 exemples 106
  17. Provectus / Kafka UI: open source Apache Kafka's Web GUI Graphical interface management tool
  18. Design pattern series: Singleton pattern
  19. Java小白入門200例105之Java ArrayList類
  20. Understanding Java record types
  21. Five load balancing algorithms implemented in Java
  22. Data structure must be an example to understand dynamic programming (with universal Python code)
  23. The idea and implementation of recursion in data structure (Python)
  24. The idea and implementation of linked list (Python)
  25. Data structure must be queue and double ended queue (Python)
  26. Idea and implementation of data structure must be able stack (Python)
  27. Data structure | time complexity (with video explanation)
  28. 20 flutter libraries you should know
  29. Case sharing: Online failure caused by Dubbo 2.7.12 bug
  30. Open source | didi open source, general functional components for Java authentication, authentication, management and task scheduling
  31. Flutter multi engine supports platformview and thread merging solution
  32. In depth understanding of netty: viewing netty traffic control from occasional downtime
  33. Spring AOP internal skill cultivation
  34. Interviewer: is Tomcat a symbolic parent delegation mechanism?
  35. Expérimentez la première tablette de consommation Linux. La puce et le système d'origine sont tous faits maison
  36. 2021 summary of the latest Java common open source libraries, Java interview handwritten code
  37. 2021 latest Java factory interview true questions, Kafka introduction video
  38. 01 javase - première connaissance de l'installation de Java et de l'environnement de développement
  39. The sales volume in September broke the record: Weilai and Xiaopeng both exceeded 10000, with an ideal month on month decrease of 24.7%
  40. Required for interview: HBase block cache
  41. Redis core principle and practice: implementation principle of hash type and dictionary structure
  42. MySQL deep dive: analyzing performance schema memory management
  43. Redis cache: kill interviewer 25 asked
  44. Solution of Chinese garbled code in idea integrated Tomcat console
  45. 2021 the latest java development interview, 46 interview questions take you to understand the advanced Java interview
  46. 2021 spring recruitment bat interview questions are explained in detail and explained clearly
  47. 2021 latest java developer learning route, 2021 latest Ali Java advanced interview questions and answers
  48. 2021 summary of the latest Java common open source libraries, the top java development tools necessary for developers
  49. Java communique avec les capteurs industriels via socket et DTu, RTU
  50. Does Hadoop have to run on Linux? (the root cause is the permission opening advantage of the operating system Linux)
  51. Java communique avec les capteurs industriels via socket et DTu, RTU
  52. 2021 summary of the latest java knowledge system. It took 8 days to finally understand JVM tuning
  53. Introduction au tissu de service Azure
  54. 【.Net vs Java? 】 先来看一下Java和C#的数据类型区别。
  55. 2021 summary of the latest java knowledge system, will you only have the technology of junior engineers
  56. 【.Net vs Java? 】 先來看一下Java和C#的數據類型區別。
  57. [.NET vs Java?] regardez d'abord les différences de type de données entre Java et C #.
  58. 2021 latest Java interview questions and answers, Java interviewer's favorite garbage collection mechanism
  59. 2021 latest Java interview written test, distributed ID generation algorithm in Seata project of source code analysis
  60. 2021 latest Java experience sharing, what java knowledge do we need to learn