一張圖看懂JVM
所以,這兩天小碼閱讀了更為詳細的資料,并對之前的內容進行了更為細化的梳理,希望這次能讓大家對JVM相關的知識點有更加深刻的理解,也歡迎大家多多批評指正。
JVM結構示意圖
JVM總體概述
JVM總體上是由類裝載子系統(ClassLoader)、運行時數據區、執行引擎、內存回收這四個部分組成。其中我們最為關注的運行時數據區,也就是JVM的內存部分則是由方法區(Method Area)、JAVA堆(Heap)、虛擬機棧(Stack)、程序計數器、本地方法棧這幾部分組成;除此以外,在概念中還有一個直接內存的概念,事實上這部分內存并不屬于虛擬機規范中定義的內存區域,但是因為在JDK1.4+后新加的NIO類,以及JDK1.8+后的Metaspace的關系,所以在討論JVM時也經常會被放到一起討論。
JVM內存概述
各內存部分的功能及具體組成部分,總結如下:
需要說明的是,堆內存是GC重點回收區域,其中分代回收機制將堆內存劃分為年輕代、老年代兩個區域,默認情況下年輕代占整個堆內存空間的1/3,而老年代則占2/3,可以通過“-XX:NewRatio”設置年輕代與老年代的比值,默認為2,表示比值年輕代與老年代的比值為“1:2”,在JVM調優時可根據應用實際情況進行調整。
而年輕代又分為Eden、Survivor0、Survivor1,這三個區域占整個新生代空間的比值為8:1:1,即Eden區占8/10,其他兩個區域分別占1/10,可通過“-XX:SurvivorRatio”參數進行設置,默認值為8。
正確理解并發問題
在了解了JVM結構,特別是內存結構后,我們再說說并發問題產生的原因。在上面的內容中我們分析了Java堆、Java棧,知道Java堆存儲的是對象,而Java棧內存是方法執行時所需要的局部變量,其中就包括堆中對象的引用,如果多個線程同時修改堆中同一引用對象的數據,就可能會產生并發問題,導致多個線程中某些線程得到的數據值與實際值不符,造成臟數據。
那么這種問題為什么會發生呢?
實際上,線程操作堆***享對象數據時并不是直接操作對象所在的那塊內存,這里稱之為主內存;而是將對象拷貝到線程私有的工作內存中進行更新,完成后再將最新的數據值同步回主內存,而多個線程在同一時刻將一個對象的值改得七七八八,然后再同時同步給對象所在的內存區域,那么以誰更新的為準就成了問題了。
所以,為了防止這種情況出現,Java提供了同步機制,確保各個線程按照一定的機制同一時刻只能運行一個線程更新主內存的值。
具體邏輯示意圖如下:
注意,這里所講的主內存、工作內存與Java內存區域中的Java堆、棧內存、方法區等并不是同一個層次的內存劃分。如果勉強類比,從變量、主內存、工作內存的定義來看,主內存主要對應于Java堆中對象實例數據部分,而工作內存則對應于虛擬機棧中使用的部分內存區域;從更低層次類比,主內存就直接對應于物理硬件的內存,而為了獲取更好的運行速度,虛擬機(甚至是硬件系統本身的優化措施)可能會讓內存優先存儲于寄存器和高速緩存中,因為程序運行時主要訪問讀寫的是工作內存。
而主內存與工作內存之間具體的交互協議,即一個變量如何從主內存拷貝到工作內存、如何從工作內存同步回主內存之間的實現細節,Java內存模型中定義了8種操作來完成。
而且還規定在執行上述8種基本操作時必須滿足如下規則:
不允許read和load、store和write操作之一單獨出現,即不允許一個變量從主內存讀取了但工作內存不接受,或者從工作內存發起了回寫了但主內存不接受的情況出現。
不允許一個線程丟棄它的最近的assign操作,即變量在工作內存中改變了之后必須把該變化同步回主內存。
不允許一個線程無原因地(沒有發生任何assign操作)把數據從線程的工作內存同步回主內存中。
一個新的變量只能在主內存中“誕生”,不允許在工作內存中直接使用一個未被初始化(load或assign)的變量,換句話說,就是對一個變量實施use、store操作之前,必須先執行過了assign和load操作。
一個變量在同一時刻只允許一條線程對其進行lock操作,但lock操作可以被同一條線程重復執行多次,多次執行lock后,只有執行相同次數的unlock操作,變量才會被解鎖。
如果對一個變量執行lock操作,那將會清空工作內存中此變量的值,在執行引擎使用這個變量前,需要重新執行load或assign操作初始化變量的值。
如果一個變量事先沒有被lock操作鎖定,那就不允許對它執行unlock操作,也不允許去unlock一個被其他線程鎖定住的變量。
對一個變量執行unlock操作之前,必須先把此變量同步回主內存中(執行store、write操作)。
以上8種內存訪問操作以及上述規則限定,再加上volatile的一些特殊規定以及final不可變特性,就已經完成確定了JAVA程序中那些內存訪問操作在并發下是安全的!
JVM參數總結
為了方便大家對于JVM有關參數有一個參照,如下:
后記
—————END—————
如果覺得小哥在認真寫文章,可以關注下公眾號,支持下哦
-結束-
近期文章推薦:
使用Kubespray部署Kubernetes集群
談談 API 網關
一個忙碌架構師的Java后端書架-2018
如何優雅的使用和理解線程池
優雅的使用 ThreadLocal
限流降級神器-哨兵(sentinel)原理分析
渣學歷小公司的我,是如何靠自己在北京買房的
Docker入門與實踐
-關注我-
https://mp.weixin.qq.com/s?__biz=MzAxNjk4ODE4OQ==&mid=2247484432&idx=1&sn=381c98c49ffb813a9b9799e232a5a42c&chksm=9bed2562ac9aac74f6a3c5cc8f8c4a145e5bd9489eadd7ef12bc687239fa5ead497dac00ce6e&scene=21#wechat_redirect
Java JVM
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。