JVM記憶體分配及申請過程
當使用new關鍵字或者其他任何方式進行建立一個類的物件時,JVM虛擬機器需要為該物件分配記憶體空間,而物件的大小在類載入完成後已經確定了,所以分配記憶體只需要在Java堆中劃分出一塊大小相等的記憶體,JVM虛擬機器中有指標碰撞和空閒列表兩種方式分配記憶體。
指標碰撞方式
如果Java堆中記憶體是規整排列的,所有被用過的記憶體放一邊,空閒的可用記憶體放一邊,中間放置一個指標作為它們的分界點,在需要為新生物件分配記憶體的時候,只要將指標向空閒記憶體那邊挪動一段與物件大小相等的距離即可分配。
代表GC回收器
ParNew,Serial,G1
空閒列表方式
如果Java堆中記憶體不是規整排列的,用過的記憶體和可用記憶體是相互交錯的,這種情況下將不能使用指標碰撞方式分配記憶體,Java虛擬機器需要維護一個列表用於記錄哪些記憶體是可用的,在為新生物件分配記憶體的時候,在列表中尋找一塊足夠大的記憶體分配,並更新列表上的記錄。
代表GC回收器
cms
Java虛擬機器選擇策略
Java虛擬機器採用哪種方式為新生物件分配記憶體,取決於所使用的垃圾收集器,當垃圾收集器具有整理過程時,虛擬機器將採用指標碰撞的方式;當垃圾收集器的回收過程沒有整理過程時,則採用空閒列表方式。
現在虛擬機器棧進行分配
此部分屬於兩部分的分配機制,當JVM建立執行緒Thread物件:
-
直接分配:區域性變數、形式參數列。
-
優化分配:逃逸分析(棧上分配、標量替換等功能)。
如果完成分配之後,則結束記憶體分配,否則出現分配失敗,或者無法進行分配操作後,會進入堆記憶體方式的分配。
新生區-Eden區的分配
TLAB記憶體的分配策略
上面剛剛說過了,主要有兩種記憶體分配機制:如果採用指標碰撞法,則會出現效能問題和指標分配衝突的問題.,JVM虛擬機器採用優化的手段,就是TLAB(ThreadLocal Allocation Buffer)預先分配了記憶體塊。
總體記憶體分配流程策略
如果TLAB記憶體分配失敗或者空間不足,則JVM會試圖為相關Java物件在Eden中初始化一塊記憶體區域,當Eden空間足夠時,記憶體申請結束
當如果出現了Eden區記憶體無法進行分配,則會發生相關MinorGC(JVM試圖釋放在Eden中所有不活躍的物件(Minor GC),釋放後若Eden空間仍然不足以放入新物件,則試圖將部分Eden中活躍物件放入Survivor區)。
此時Survivor區被用來作為Eden及old的中間交換區域,如果Survivor不足以放置eden區的物件 ,會進行擔保分配,或者已經達到直接晉升到老年代的條件後,此時如果old區有空閒,Survivor區的物件會被移到Old區。
當old區空間不夠時,JVM會在old區進行major collection;
完全垃圾收集後,若Survivor及old區仍然無法存放從Eden複製過來的部分物件,導致JVM無法在Eden區為新物件建立記憶體區域,則出現"Out of memory錯誤";
- jvm優先分配在eden區
- 當Eden空間足夠時,記憶體申請結束。
JVM鎖的膨脹執行流程機制
無鎖節點/偏向鎖階段
建立執行緒的時候在程式執行到同步程式碼塊的時候,首先會基於上面說到的記憶體分配策略進行分配記憶體,此時會當當前執行緒獲取到了相關鎖資源的時候,因為屬於無鎖狀態下轉換為偏向鎖:
無鎖標記頭
偏向鎖標記頭
- 會將相關的當前執行緒的執行緒ID賦值到相關的標記欄位中。
- 為了提高效能以及棧空間可以獲取相關的競爭資料,會將物件頭的標記欄位(Markword)拷貝到棧空間內部(Lock Record)鎖記錄。
輕量級鎖階段
競爭到鎖的執行緒
與此同時,當另外一個執行緒同時也去競爭該資源的時候,需要進行競爭,因為在獲取資源的時候,底層採用CAS機制取獲取相關的資源標誌,一旦獲取成功,便可以通過偏向鎖標識進行判斷是否屬於當前的鎖owner執行緒。
當發現不屬於偏向鎖的執行緒進來競爭的時候,此時會產生競爭關係,因為同一時刻,只能允許一個執行緒獲取資源,當前獲取資源的執行緒會因為有其他執行緒也爭搶過該資源,故此將java物件頭中的鎖欄位改為00,如下圖所示:
未競爭到鎖的執行緒
當發生執行緒爭搶CAS機制失敗的時候,會進行相關的自旋機制,進行嘗試下一次進行爭搶到鎖。
重量級鎖階段
未競爭到鎖的執行緒
-
當超過自旋的執行緒一直處於自旋,且超過了自旋閾值之後,變會升級成為了重量級鎖。
或 -
當更多的執行緒都處於爭搶狀態且屬於自旋鎖機制之後(出現了大量的輕量級鎖之後),便會升級未重量級鎖。
-
直到被喚醒重新映象競爭鎖資源資訊。
升級為重量級鎖的結果會將執行緒的標誌位置為10
此時不會在進行自旋CAS爭搶 ,而是直接阻塞執行(採用底層mutex/Fast Mutex鎖進行暫停中斷執行緒的執行)。
競爭到鎖的執行緒
-
當鎖標記因為發生變化,成為了重量級鎖,所以,執行緒會同步自己的所記錄,發現不一致,同步為重量級鎖狀態後,釋放鎖之後,進行喚醒阻塞的狀態的執行緒。
-
鎖的狀態暫時處於重量級鎖狀態。接下來會專門寫一篇文章講解一下鎖降級哦,鎖降級會較為複雜,而且場景完全不一樣,對JVM要求也不一樣。