成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊聊Java對象棧上分配

開發 前端
假設Java堆中內存是絕對規整的,所有用過的內存都放在一邊,空閑的內存放在另一邊,中間放著一個指針作為分界點的指示器,那所分配內存就僅僅是把那個指針向空閑空間那邊挪動一段與對象大小相等的距離,這種分配方式稱為“指針碰撞”(Bump the Pointer)。

通過對象的分配過程分析,除了堆以外,還有兩個地方可以存放對象:

棧和TLAB(Thread Local Allocation Buffer)。

Java對象分配流程圖:

如果開啟棧上分配,JVM會先進行棧上分配,如果沒有開啟棧上分配或則不符合條件的則會進行TLAB分配,如果TLAB分配不成功,再嘗試在eden區分配,如果對象滿足了直接進入老年代的條件,那就直接分配在老年代。

棧上分配

在JVM中,堆是線程共享的,因此堆上的對象對于各個線程都是共享和可見的,只要持有對象的引用,就可以訪問堆中存儲的對象數據。虛擬機的垃圾收集系統可以回收堆中不再使用的對象,但對于垃圾收集器來說,無論篩選可回收對象,還是回收和整理內存都需要耗費時間。

如果確定一個對象的作用域不會逃逸出方法之外,那可以將這個對象分配在棧上,這樣,對象所占用的內存空間就可以隨棧幀出棧而銷毀。在一般應用中,不會逃逸的局部對象所占的比例很大,如果能使用棧上分配,那大量的對象就會隨著方法的結束而自動銷毀了,無須通過垃圾收集器回收,可以減小垃圾收集器的負載。

JVM允許將線程私有的對象打散分配在棧上,而不是分配在堆上。分配在棧上的好處是可以在函數調用結束后自行銷毀,而不需要垃圾回收器的介入,從而提高系統性能。

棧上分配的技術基礎:

一是逃逸分析:逃逸分析的目的是判斷對象的作用域是否有可能逃逸出函數體。關于逃逸分析的問題可以看我另一篇文章:

二是標量替換:允許將對象打散分配在棧上,比如若一個對象擁有兩個字段,會將這兩個字段視作局部變量進行分配。

只能在server模式下才能啟用逃逸分析,參數-XX:DoEscapeAnalysis啟用逃逸分析,參數-XX:+EliminateAllocations開啟標量替換(默認打開)。Java SE 6u23版本之后,HotSpot中默認就開啟了逃逸分析,可以通過選項-XX:+PrintEscapeAnalysis查看逃逸分析的篩選結果。

TLAB(Thread Local Allocation Buffer)

`TLAB的全稱是Thread Local Allocation Buffer,即線程本地分配緩存區,這是一個線程專用的內存分配區域。

由于對象一般會分配在堆上,而堆是全局共享的。因此在同一時間,可能會有多個線程在堆上申請空間。因此,每次對象分配都必須要進行同步(虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性),而在競爭激烈的場合分配的效率又會進一步下降。JVM使用TLAB來避免多線程沖突,在給對象分配內存時,每個線程使用自己的TLAB,這樣可以避免線程同步,提高了對象分配的效率。

TLAB本身占用eEden區空間,在開啟TLAB的情況下,虛擬機會為每個Java線程分配一塊TLAB空間。參數-XX:+UseTLAB開啟TLAB,默認是開啟的。TLAB空間的內存非常小,缺省情況下僅占有整個Eden空間的1%,當然可以通過選項-XX:TLABWasteTargetPercent設置TLAB空間所占用Eden空間的百分比大小。

由于TLAB空間一般不會很大,因此大對象無法在TLAB上進行分配,總是會直接分配在堆上。TLAB空間由于比較小,因此很容易裝滿。比如,一個100K的空間,已經使用了80KB,當需要再分配一個30KB的對象時,肯定就無能為力了。這時虛擬機會有兩種選擇,第一,廢棄當前TLAB,這樣就會浪費20KB空間;第二,將這30KB的對象直接分配在堆上,保留當前的TLAB,這樣可以希望將來有小于20KB的對象分配請求可以直接使用這塊空間。實際上虛擬機內部會維護一個叫作refill_waste的值,當請求對象大于refill_waste時,會選擇在堆中分配,若小于該值,則會廢棄當前TLAB,新建TLAB來分配對象。

這個閾值可以使用TLABRefillWasteFraction來調整,它表示TLAB中允許產生這種浪費的比例。默認值為64,即表示使用約為1/64的TLAB空間作為refill_waste。默認情況下,TLAB和refill_waste都會在運行時不斷調整的,使系統的運行狀態達到最優。如果想要禁用自動調整TLAB的大小,可以使用-XX:-ResizeTLAB禁用ResizeTLAB,并使用-XX:TLABSize手工指定一個TLAB的大小,-XX:+PrintTLAB可以跟蹤TLAB的使用情況。一般不建議手工修改TLAB相關參數,推薦使用虛擬機默認行為。`

所謂TLAB其實就是這樣的一個東西:(簡化偽代碼)

struct ThreadLocalAllocBuffer {
HeapWord* _start;

HeapWord* _top;

HeapWord* _end;

};

每個線程會從Eden分配一大塊空間,例如說100KB,作為自己的TLAB。這個start是TLAB的起始地址,end是TLAB的末尾,然后top是當前的分配指針。顯然start <= top < end。

在Eden分配空間時,用的是bump-the-pointer方式來分配,但由于Eden是所有Java線程所共享的,在bump pointer的時候必須加鎖(或者CAS)才可以保證安全;而當每個線程從Eden分配到一塊空間當作TLAB來用之后,在TLAB里分配小塊空間同樣是bump-the-pointer(上面示意的top指針)則不需要加鎖。 當一個Java線程在自己的TLAB中分配到盡頭之后,再要分配就會出發一次“TLAB refill”,也就是說之前自己的TLAB就“不管了”(所有權交回給共享的Eden),然后重新從Eden里分配一塊空間作為新的TLAB。所謂“不管了”并不是說就讓舊TLAB里的對象直接死掉,而是把那塊空間的控制權歸還給普通的Eden,里面的對象該怎樣還是怎樣。

通常情況下,在TLAB中分配多次才會填滿TLAB、觸發TLAB refill,這樣使用TLAB分配就比直接從共享部分的Eden分配要均攤(amortized)了同步開銷,于是提高了性能。其實很多關注多線程性能的malloc庫實現也會使用類似的做法,例如TCMalloc。

到觸發GC的時候,無論是minor GC還是full GC,要收集Eden的時候里面的空間無論是屬于某個線程的TLAB還是不屬于任何TLAB都一視同仁,把Eden當作一個整體來收集里面的對象——把活的對象拷貝到survivor space(或者直接晉升到Old Gen)。在GC結束之后,每個Java線程又會重新從Eden分配自己的TLAB。周而復始。

想像這樣的代碼:

public class Test {
public static Test sharedStatic;
public Test sharedInstanceField;
public static void foo() {
Test localVar = new Test(); // 1
if (sharedStatic == null) {
sharedStatic = localVar; // 2
} else {
sharedStatic.sharedInstanceField = localVar; // 3
}
}
}

(這個例子純粹為了示意“獨占”與“共享”的概念,請不要吐槽線程安全問題 ),我們在 (1) 創建了一個新的Test實例。如題主所說,在啟動UseTLAB(默認開啟)的時候,這個Test實例會被分配在當前執行Test.foo()的線程的TLAB里。TLAB在執行分配動作的時候要更新top指針,而更新這個指針不需要加任何鎖。

對象內存分配的兩種方法

為對象分配空間的任務等同于把一塊確定大小的內存從Java堆中劃分出來。

指針碰撞(Serial、ParNew等帶Compact過程的收集器)

假設Java堆中內存是絕對規整的,所有用過的內存都放在一邊,空閑的內存放在另一邊,中間放著一個指針作為分界點的指示器,那所分配內存就僅僅是把那個指針向空閑空間那邊挪動一段與對象大小相等的距離,這種分配方式稱為“指針碰撞”(Bump the Pointer)。

空閑列表(CMS這種基于Mark-Sweep算法的收集器)

如果Java堆中的內存并不是規整的,已使用的內存和空閑的內存相互交錯,那就沒有辦法簡單地進行指針碰撞了,虛擬機就必須維護一個列表,記錄上哪些內存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,并更新列表上的記錄,這種分配方式稱為“空閑列表”(Free List)。

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2021-03-22 11:51:22

Java內存棧上

2021-09-28 07:12:09

函數內存

2022-10-08 08:01:07

JVMTLABPLAB

2019-07-23 15:04:54

JavaScript調用棧事件循環

2021-12-16 06:52:33

C語言內存分配

2022-11-30 08:19:15

內存分配Go逃逸分析

2022-02-11 09:31:23

IPV4IP地址IANA

2021-12-02 09:13:56

序列壓入

2015-11-16 11:22:05

Java對象內存分配

2009-06-03 15:52:34

堆內存棧內存Java內存分配

2020-11-26 18:18:21

微服務業務規模技術

2023-12-28 09:55:08

隊列數據結構存儲

2018-03-27 10:06:26

對象存儲演進

2024-01-31 09:50:10

JVM逃逸分析HotSpot

2023-03-26 00:43:42

JVM對象測試

2025-06-05 08:05:00

vectorC++對象存儲

2018-02-08 14:57:22

對象內存分配

2022-03-16 08:39:19

StackHeap內存

2022-09-07 07:27:36

函數元素

2013-05-21 09:08:24

服務器虛擬化網卡
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 中文字幕不卡在线观看 | 亚洲欧美一区二区三区国产精品 | 亚洲精品 在线播放 | 婷婷久 | aaa精品 | 日本久久久久久 | 99精品99久久久久久宅男 | 色久五月 | 亚洲精品一区中文字幕乱码 | 亚洲精品久久久9婷婷中文字幕 | 日本淫视频 | 一二三区在线 | 久久在线精品 | 日韩在线免费视频 | 久久国产日韩欧美 | 操久久 | 伊人无码高清 | 日本国产欧美 | 日韩av一区二区在线观看 | 日韩在线小视频 | 91综合网| 蜜桃精品视频在线 | 亚洲精品在线免费看 | 国产高清免费视频 | 国产9久| 国产伦精品一区二区 | 一区在线观看 | 午夜视频在线观看视频 | 粉嫩国产精品一区二区在线观看 | 亚洲黄色片免费观看 | 色橹橹欧美在线观看视频高清 | 亚洲成人av | 色偷偷888欧美精品久久久 | 国产精品一区二区在线 | 成人美女免费网站视频 | 国产精品区二区三区日本 | 精品国产乱码久久久久久a丨 | 91九色视频在线 | 中文字幕乱码亚洲精品一区 | 亚洲视频 欧美视频 | 国产91在线 | 欧美 |