指針碰撞、空閑列表和內存分配的並發安全問題的底層秘密

程序員小迷 2024-04-30 11:57:41

一、指針碰撞

以虛擬機爲新生代對象分配內存爲例。爲對象分配空間的任務等同于把一塊確定大小的內存從Java堆中劃分出來。

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

指針碰撞適用于Serial和ParNew等不會産生內存碎片的垃圾收集器。

新生代通常使用指針碰撞進行內存分配。

二、空閑列表

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

選擇哪種分配方式由Java堆是否規整決定,而Java堆是否規整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。

空閑列表適用于CMS等可能會産生內存碎片的垃圾收集器。

老年代通常使用空閑列表進行內存分配。

三、TLAB

指本地線程分配緩沖(Thread Local Allocation Buffer,TLAB)。

1.TLAB的目的是在爲新對象分配內存空間時,讓每個Java應用線程能使用自己專屬的分配指針來分配空間,減少同步開銷。

2.TLAB只是讓每個線程擁有私有的分配指針,但底下存對象的內存空間還是給所有線程訪問的,只是其它線程無法在這個區域分配而已。當一個TLAB用滿(分配指針top撞上分配極限end了),就新申請一個TLAB。

四、內存分配的並發安全問題

1.分配內存的並發安全問題的解決方法有兩種方案:

1)一種是對分配內存空間的動作進行同步處理——實際上虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性。

2)另一種是把內存分配的動作按照線程劃分在不同的空間中進行,即每個線程在Java堆中預先分配一小塊私有內存,也就是本地線程分配緩沖(Thread Local Allocation Buffer,TLAB),如果設置了虛擬機參數 -XX:UseTLAB,在線程初始化時,同時也會申請一塊指定大小的內存,只給當前線程使用,這樣每個線程都單獨擁有一個Buffer,如果需要分配內存,就在自己的Buffer上分配,這樣就不存在競爭的情況,可以大大提升分配效率,當Buffer容量不夠的時候,再重新從Eden區域申請一塊繼續使用。

微風不燥,陽光正好,你就像風一樣經過這裏,願你停留的片刻溫暖舒心。

我是程序員小迷(致力于C、C++、Java、Kotlin、Android、Shell、JavaScript、TypeScript、Python等編程技術的技巧經驗分享),若作品對您有幫助,請關注、分享、點贊、收藏、在看、喜歡,您的支持是我們爲您提供幫助的最大動力。

歡迎關注。助您在編程路上越走越好!

0 阅读:14

程序員小迷

簡介:致力于Android、C等編程技術的技巧經驗分享