日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

lisp 标记形心_标记-压缩算法

發布時間:2025/4/16 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 lisp 标记形心_标记-压缩算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

內存碎片一直是非移動垃圾回收器(指在垃圾回收時不進行對象的移動)的一個問題,比如說在前面的標記-清除垃圾回收器就有這樣的問題。而標記-壓縮垃圾回收算法能夠有效的緩解這一問題。

算法原理

既然叫標記-壓縮算法,那么它也分為兩個階段,一個是標記(mark),一個是壓縮(compact). 其中標記階段跟標記-清除算法中的標記階段是一樣的,可以參考前面的文章。

而對于壓縮階段,它的工作就是移動所有的可達對象到堆內存的同一個區域中,使他們緊湊的排列在一起,從而將所有非可達對象釋放出來的空閑內存都集中在一起,通過這樣的方式來達到減少內存碎片的目的。

在壓縮階段,由于要移動可達對象,那么需要考慮移動對象時的順序,一般分為下面三種:

任意順序 - 即不考慮原先對象的排列順序,也不考慮對象間的引用關系,隨意的移動可達對象,這樣可能會有內存訪問的局部性問題。

線性順序 - 在重新排列對象時,會考慮對象間的引用關系,比如A對象引用了B對象,那么就會盡可能的將A,B對象排列在一起。

滑動順序 - 顧名思義,就是在重新排列對象時,將對象按照原先堆內存中的排列順序滑動到堆的一端。

現在大多數的垃圾收集算法都是按照任意順序或滑動順序去實現的。下面我們分別來看下它們各自的算法原理。

Two-Finger 算法

Two-Finger算法來自Edwards, 它在壓縮階段移動對象時是任意順序移動的,它最適用于處理包含固定大小對象的內存區域。由于Mark階段都是跟標記-清除算法一致的,這里我們只關注Compact階段。

Two-Finger算法是一個Two Passes算法,即需要遍歷堆內存兩次,第一次遍歷是將堆末尾的可達對象移動到堆開始的空閑內存單元去,第二次遍歷則需要修改可達對象的引用,因為一些可達對象已經被移動到別的地址,而原先引用它們的對象還指向著它們移動前的地址。

在這兩次遍歷過程中,首尾兩個指針分別從堆的頭尾兩個位置向中間移動,直至兩個指針相遇,由于它們的運動軌跡酷似兩根手指向中間移動的軌跡,因此稱為Two Finger算法。

第一次遍歷

下面我們先看下第一遍遍歷的偽代碼 - 來自<>,

compact():

relocate(HeapStart,HeapEnd)

updateReferences(HeapStart,free)

relocate(start,end)

free

scan

while free < scan

//找到一個可以被釋放的空間

while isMarked(free)

unsetMarked(free)

free

//找到一個可以移動的可達對象

while not isMarked(scan) && scan > free

scan

if scan > free

unsetMarked(scan)

move(scan, free) //將scan位置的可達對象移動到free位置上

*scan

free

scan

第一次遍歷的原理是,頭指針(free)沿著堆頭向堆尾前進,直到找到一個空閑的內存單元(即沒有被標記為可達對象的內存單元),如遇到可達對象,則清除其標記。接著尾指針(scan)從堆尾向堆頭方向前進,直到找到一個被標記為可達的內存單元。最后,collector將可達對象從尾指針(scan)指向的位置移動到頭指針(free)指向的位置,最后將可達對象移動后的位置(當前free指針指向的位置)寫到原先可達對象處于的位置(當前尾指針scan指向的位置), 為下一次的遍歷 - 更新對象相互間的引用做好準備。注:當移動可達對象時,其引用的對象在可達對象移動后保持不變,如下圖中的G對象移動后依然指向位置5和位置10。

第一次遍歷

第二次遍歷

而第二次的遍歷則是為了更新引用關系,一個可達對象可以被其他對象引用,比如上圖中的K對象,如果其被移動后,引用它的對象比如說G并不知道它被移動了,那么這第二次的遍歷就是為了告訴G它所引用的對象K已經被移動到新的位置上去了,它需要更新它對K的引用。

updateReferences(start,end)

for each fld in Roots //先更新mutator根對象所引用的對象關系

ref

if ref >= end

*fld

scan

while scan < ned

for each fld in Pointers(scan)

ref

if ref >= end

*fld

scan

第二次遍歷,collector先會對根對象進行遍歷,比如根對象2引用著位置6的內存單元,根據算法,該位置大于等于end指針所指向的位置 - 即第一次遍歷free指針和scan指針相遇的位置,那么我們就認為這個位置的對象已經被移動,需要更新根對象2的引用關系,即從引用位置6改為引用位置2(位置6的內存單元中記錄著該對象被移動后的新位置)。同理,在移動G對象的時候,也是要判斷看G所引用的內存單元位置是否大于end指針指向的位置,如果小于,則不處理。否則則修改G的引用關系。

第二次遍歷

LISP2 算法

Lisp2算法是一種應用更為廣泛的壓縮算法,它屬于滑動順序算法中的一種。它跟Two-Finger算法的不同還在于它可以處理不同大小的對象,而不再是固定大小的對象。同時,計算出來的可達對象的遷移地址需要額外的空間進行存儲而不再是復寫原先對象所在的位置。最后,Lips2算法需要進行3次堆內存的遍歷。

第一次遍歷

第一次遍歷,collecor僅僅是計算和記錄可達對象應該遷移去的地址。

compact():

computeLocations(HeapStart,HeapEnd,HeapStart)

updateReferences(HeapStart,HeapEnd)

relocate(HeapStart,HeapEnd)

computeLocations(start,end,toRegion):

scan

free

while scan < end

if isMarked(scan)

forwardingAddress(scan)

free

scan

第一次遍歷

指針free, scan同時指向堆起始位置,同時scan指針向堆尾移動,目的是要找到被標記的可達對象。

找到可達對象后,在scan指針對應的位置分配一個額外的空間來存儲該可達對象應該遷移到的地址 - 就是free指針指向的位置0,同時free指針向堆尾移動B對象大小的距離- free'指針指向的位置。最后scan指針繼續往前走,直到尋找到下一個可達對象D - scan'指針指向的位置。

同理,在可達對象D處分配一塊空間來保存對象D應該遷移到的位置,由于B對象已經占用了2個內存單元,所以對象E的遷移地址是從位置2開始,也就是當前free指針指向的位置。

指針free,scan繼續向前移動。

第一次遍歷完后,所有的可達對象都有了對應的遷移地址,free指針指向位置9,因為所有的可達對象總共占了9個單元大小的空間。

第二次遍歷

第二次遍歷主要是修改對象間的引用關系,基本跟Two Finger算法的第二次遍歷一樣。

updateReferences(start,end):

for each fld in Roots

ref

if ref != null

*fld

scan

while scan < end

if isMarked(scan)

for each fld in Pointers(scan)

if *fld != null

*fld

scan

第二次遍歷

修改根對象的引用關系,根對象1引用對象B,對象B的遷移地址為0,于是collector將根對象對B對象的引用指向它的遷移地址 - 位置0, 現在A對象所處的位置。

同理,對于根對象2,3都執行同樣的操作,將它們對其所引用的對象的引用修改為對應的它們所引用的對象的遷移地址。

通過scan指針遍歷堆內存,更新所有的可達對象對其引用對象的引用為其引用對象的遷移地址。比如說,對于可達對象B, 它引用了對象D,D的遷移地址是2,那么B直接將其對D對象的引用重新指向2這個位置。

第二次遍歷結束后的對象之間的引用關系。

第三次遍歷

第三次遍歷則是根據可達對象的遷移地址去移動可達對象,比如說可達對象B,它的遷移地址是0,那么就將其移動到位置0,同時去除可達對象的標記,以便下次垃圾收集。

relocate(start,end):

scan

while scan < end

if isMarked(scan)

dest

move(scan,dest) //將可達對象從scan位置移動到dest位置

unsetMarked(dest)

scan

所有可達對象移動結束后,內存單元展示為:

第三次遍歷

后記

通過上面的兩種算法的展示,我想大家應該都大概了解了標記-壓縮算法工作的基本原理,除了上面兩種實現,還有其它的一些壓縮算法的實現,這里就不一一陳列。有興趣者可以自己到網絡上搜索。

標記-壓縮算法雖然緩解的內存碎片問題,但是它也引用了額外的開銷,比如說額外的空間來保存遷移地址,需要遍歷多次堆內存等。

總結

以上是生活随笔為你收集整理的lisp 标记形心_标记-压缩算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。