<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 42  文章 - 71  trackbacks - 0
    <2008年4月>
    303112345
    6789101112
    13141516171819
    20212223242526
    27282930123
    45678910

    常用鏈接

    留言簿

    隨筆檔案

    文章分類

    文章檔案

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

     

    3 分配

    3.1 鎖堆分配

    當(dāng)所需要的空間大于512字節(jié)或者在已有的緩存中無法分配所需的大小,就會(huì)發(fā)生鎖堆分配,顧名思義,鎖堆分配需要獲取堆上的鎖,所以應(yīng)該盡量避免。

    If size < 512 or enough space in cache
                try cacheAlloc
                return if OK
    HEAP_LOCK
    Do
                If there is a big enough chunk on freelist
                            takeit
                            goto Got it
                else
                            manageAllocFailure
                            if any error
                                        goto Get out
    End Do
    Cot it:
    Initialise object
    Get out:
    HEAP_UNLOCK

    圖表 7 鎖堆分配

    圖表7即是鎖堆分配的偽代碼,垃圾收集器首先檢測(cè)要分配的空間的大小,如果小于512字節(jié)或者在當(dāng)前緩存內(nèi)有足夠的空間進(jìn)行分配,則嘗試緩存分配;如果不能進(jìn)行緩存分配,或者緩存分配失敗,則會(huì)發(fā)生鎖堆。垃圾收集器開始在空閑鏈表中查詢可用的空間,如果找到滿足條件的空間,則在此空間內(nèi)進(jìn)行分配,并且將剩余的空間返回到空閑鏈表中。需要注意的是,如果剩余的空閑空間不足512字節(jié)加上頭信息大小(32位架構(gòu)上12字節(jié),64架構(gòu)上24字節(jié)),那么就不會(huì)返回到空閑鏈表中,這些小的存儲(chǔ)空間就被稱為“暗物質(zhì)”。如果垃圾收集器無法找到滿足條件的空閑空間,就會(huì)發(fā)生分配失敗,開始進(jìn)行垃圾收集工作。垃圾收集結(jié)束之后,繼續(xù)在空閑鏈表中查找滿足需求的空間,如果依然無法找到,就會(huì)發(fā)生內(nèi)存溢出的錯(cuò)誤。無論對(duì)象是否分配成功,堆鎖都會(huì)被釋放。

    3.1.1 小提示

    在某些情況下,比如在很大的堆中,空閑鏈表是由大量的空閑存儲(chǔ)空間段組成,或者應(yīng)用頻繁的申請(qǐng)較大的存儲(chǔ)空間,鎖堆分配策略可能會(huì)存在問題。因?yàn)槊看味夹枰獜目臻e鏈表的頭開始查找滿足需求的空閑空間,效率相對(duì)較低,所以有了快速空閑鏈表查找算法來解決這個(gè)問題。

    每次鎖堆分配嘗試查找鏈表時(shí),都會(huì)收集以下數(shù)據(jù):

    · 在找到滿足需求的空間之前已經(jīng)在空閑鏈表中查找過的存儲(chǔ)段數(shù)量

    · 在找到滿足需求的空間之前所查找的空閑段中的最大段的大小。也就是不能滿足需求的段的最大大小

    當(dāng)找到滿足需求的空間之后,如果查找計(jì)數(shù)大于20,表示需要?jiǎng)?chuàng)建一個(gè)active hint指向空閑鏈表。然后,根據(jù)實(shí)際的需求來決定是從空閑鏈表頭開始查找,還是從active hint指向的位置開始查找,一旦在一個(gè)段中進(jìn)行了分配,active hint會(huì)被及時(shí)更新。

    3.2 緩存分配

    image013

    圖表 8 堆中的緩存段

    緩存分配是針對(duì)小對(duì)象設(shè)計(jì)的高性能分配策略,線程預(yù)先從堆中分配一段空間,對(duì)象直接在這一段空間內(nèi)進(jìn)行分配,無需獲取堆的鎖,所以緩存分配的效率是極高的。如果滿足以下條件,就會(huì)進(jìn)行緩存分配:

    · 對(duì)象小于512字節(jié),或者

    · 線程本地堆中有足夠的空間容納對(duì)象

    圖表8是堆中緩存段的示意圖。緩存段又叫做線程本地堆(thread local heap,TLH)。當(dāng)垃圾收集器為一個(gè)線程分配線程本地堆時(shí),使用鎖堆分配一段空間給該線程獨(dú)享使用。分配集合中對(duì)應(yīng)線程本地堆的比特位不會(huì)被設(shè)置,直到線程本地堆滿了,或者一輪垃圾收集工作開始的時(shí)候,才會(huì)被設(shè)置。為了提高分配線程本地堆的性能,垃圾收集器總是使用在空閑鏈表中找到的第一個(gè)空閑段,并且不大于40KB。

     image015

    圖表 9 在緩存段中分配的對(duì)象

    圖表9顯示的是在緩存段中分配的一些對(duì)象。在緩存段中,對(duì)象總是從段頂開始分配,這樣能夠比從段底分配更加高效。圖表9中還顯示了在分配集合中,并沒有設(shè)置緩存段的標(biāo)識(shí)位。直到緩存段滿了,或者一輪垃圾收集工作開始的時(shí)候,這些標(biāo)識(shí)位才會(huì)被設(shè)置。

    4 垃圾收集

    在鎖堆分配中如果發(fā)生了分配失敗,或者有對(duì)System.gc()的顯式調(diào)用,則會(huì)發(fā)生垃圾收集。調(diào)用System.gc()的線程或者發(fā)生分配失敗的線程負(fù)責(zé)進(jìn)行垃圾收集。首先,他獲取垃圾收集所需的鎖,然后掛起其他線程,再開始垃圾收集的三個(gè)階段的工作:標(biāo)識(shí)、清理以及壓縮階段(非必須的)。IBM JDK的垃圾收集是stop-the-world類型的,因?yàn)樵诶占^程中,所有的應(yīng)用線程都被掛起。

    4.1 標(biāo)識(shí)階段

    在標(biāo)識(shí)階段,所有的活動(dòng)對(duì)象都被標(biāo)識(shí)。因?yàn)椴豢傻竭_(dá)對(duì)象不太容易定位,所以要明確所有的可到達(dá)對(duì)象,那么余下的就是垃圾了。這個(gè)標(biāo)識(shí)所有可達(dá)到對(duì)象的過程也被稱作追蹤(tracing)。

    被保存的寄存器、線程的執(zhí)行棧、類中的靜態(tài)域、本地或者全局的JNI引用,共同構(gòu)成了虛擬機(jī)的活動(dòng)狀態(tài)。虛擬機(jī)自身調(diào)用的函數(shù)都會(huì)生成一個(gè)C執(zhí)行棧上的一個(gè)幀。這個(gè)幀可能包含一些對(duì)象實(shí)例,可能是要賦值給本地變量的對(duì)象,也可能是來自調(diào)用者的調(diào)用參數(shù)。在追蹤階段,所有這些引用都是被同等對(duì)待的。垃圾收集器自頂?shù)降讙呙杳總€(gè)線程的棧,4字節(jié)為一組(在64位架構(gòu)上是8字節(jié)一組),垃圾收集器假設(shè)棧是4字節(jié)對(duì)齊的(在64位架構(gòu)上是8字節(jié)對(duì)齊的),然后檢查棧上每個(gè)4字節(jié)組是否是指向堆上的一個(gè)對(duì)象,有可能所指向的不是一個(gè)真正的對(duì)象,因?yàn)榭赡苤皇桥銮珊鸵粋€(gè)整數(shù)或者浮點(diǎn)數(shù)的存儲(chǔ)表示相同。垃圾收集器掃描線程棧,然后保守的處理他所找到的這些指針,只要這個(gè)指針指向一個(gè)對(duì)象地址,那么就假設(shè)他真的是一個(gè)對(duì)象引用,并且在垃圾收集的時(shí)候,不能移動(dòng)這個(gè)對(duì)象。如果滿足以下3個(gè)條件,這個(gè)槽位就被認(rèn)為是指向一個(gè)對(duì)象的指針:

    1. 8字節(jié)對(duì)齊的

    2. 位于堆的地址范圍內(nèi)

    3. 對(duì)應(yīng)的分配比特位已經(jīng)設(shè)置為1

    以這種方式被引用的對(duì)象就是根對(duì)象,根對(duì)象的dosed標(biāo)識(shí)位被設(shè)置,表示這個(gè)對(duì)象不能被移動(dòng),只有在壓縮階段時(shí),垃圾收集器才會(huì)設(shè)置dosed標(biāo)識(shí)位。從根對(duì)象開始,可以精確的追蹤其他被引用對(duì)象,因?yàn)槔占髦肋@些引用確實(shí)是真正指向?qū)ο蟮囊茫捎诳梢孕薷囊茫赃@些被引用的對(duì)象在壓縮階段可以被移動(dòng)。追蹤階段使用一個(gè)可以容納4KB條目的棧,所有的引用都被壓棧,同時(shí),設(shè)置對(duì)應(yīng)的標(biāo)識(shí)比特位。首先,全部根對(duì)象被壓棧,然后再依次出棧,在出棧的過程中繼續(xù)追蹤。普通對(duì)象(非數(shù)組對(duì)象)通過mptr訪問類信息塊來追蹤被其引用的其他對(duì)象。一旦找到引用,并且被引用對(duì)象尚未被標(biāo)識(shí),那么該對(duì)象就被標(biāo)識(shí)并且壓棧。

    對(duì)于數(shù)組對(duì)象,垃圾收集器檢查每個(gè)條目,如果該對(duì)象尚未被標(biāo)識(shí),則對(duì)其進(jìn)行標(biāo)識(shí)并且壓棧。為了避免標(biāo)識(shí)棧溢出,每次只處理數(shù)組的一部分內(nèi)容。

    垃圾收集器重復(fù)以上過程,直到標(biāo)識(shí)棧為空。

    4.1.1 標(biāo)識(shí)棧溢出

    因?yàn)闃?biāo)識(shí)棧的大小是有限制的,所有有可能發(fā)生標(biāo)識(shí)棧溢出的問題。雖然這種問題發(fā)生的幾率非常小,但是當(dāng)發(fā)生標(biāo)識(shí)棧溢出時(shí),對(duì)于垃圾收集的暫停時(shí)間有非常大的影響。

    4.1.1.1 溢出集合

    垃圾收集器需要一個(gè)能夠映射整個(gè)堆的比特位數(shù)組來記錄堆中未被追蹤的對(duì)象,就是FR_bits數(shù)組,該數(shù)組是為進(jìn)行增量壓縮(Incremental Compaction, IC)設(shè)置的,對(duì)于每個(gè)可能的引用槽位(在32位架構(gòu)上是4字節(jié),在64位架構(gòu)上是8字節(jié)),有一個(gè)對(duì)應(yīng)的比特位。由于JVMObject頭信息不會(huì)包含任何引用信息,所以每個(gè)對(duì)象對(duì)應(yīng)的FR_bits數(shù)組中的前2個(gè)比特位是不被IC使用的,因此垃圾收集器使用FR_bits數(shù)組中的第1個(gè)冗余比特位實(shí)現(xiàn)溢出集合。

    4.1.1.2 處理非系統(tǒng)堆對(duì)象的標(biāo)識(shí)棧溢出

    當(dāng)線程嘗試將一個(gè)引用壓棧到標(biāo)識(shí)棧時(shí),如果此時(shí)發(fā)現(xiàn)標(biāo)識(shí)棧已經(jīng)滿了,他會(huì)向自己的本地標(biāo)識(shí)隊(duì)列發(fā)布一個(gè)任務(wù)。如果發(fā)布動(dòng)作失敗了,線程會(huì)設(shè)置這個(gè)引用對(duì)象對(duì)應(yīng)的FR_bits數(shù)組,以表示發(fā)生了標(biāo)識(shí)棧溢出。

    然后追蹤工作繼續(xù)處理已經(jīng)設(shè)置了FR_bits數(shù)組的、無法被壓棧的引用。

    一旦線程處理完了標(biāo)識(shí)棧,他就會(huì)嘗試接管溢出集合,并且,為了確保溢出集合只被一個(gè)線程處理,該線程設(shè)置一個(gè)是否發(fā)生標(biāo)識(shí)棧溢出的全局標(biāo)識(shí)為False。一旦確立了溢出集合的所屬權(quán),這個(gè)線程就開始掃描FR_bits數(shù)組,查找所有的非零比特位。一旦找到非零的比特位,則將其清零,并且對(duì)應(yīng)的引用被壓棧。到一定量的引用被壓棧之后,他們被發(fā)布到本地標(biāo)識(shí)隊(duì)列,以便于其他線程輔助處理溢出集合。

    在處理溢出集合的同時(shí),有可能發(fā)生標(biāo)識(shí)棧溢出。如果發(fā)生這種情況,那么一個(gè)全局標(biāo)識(shí)會(huì)被設(shè)置以標(biāo)識(shí)發(fā)生了溢出,上面描述的處理過程會(huì)重復(fù)執(zhí)行。

    4.1.1.3 系統(tǒng)堆溢出機(jī)制

    在收集根對(duì)象時(shí),垃圾收集器會(huì)將所有系統(tǒng)堆和ACS堆中的對(duì)象引用也壓棧,因此同樣會(huì)發(fā)生標(biāo)識(shí)棧溢出的問題。但是FR_bits數(shù)組只映射了非系統(tǒng)堆,所以無法用來記錄系統(tǒng)堆和ACS堆中未被追蹤的對(duì)象。

    在垃圾收集的標(biāo)識(shí)階段,已經(jīng)加載的類的地址是不會(huì)被修改的,因此,垃圾收集器需要記錄在發(fā)生標(biāo)識(shí)棧溢出前的那一刻,他所到達(dá)的追蹤鏈條的位置。所以,引入兩個(gè)全局變量“overflowSystemClasses”和“overflowACSClasses”來表示對(duì)應(yīng)在系統(tǒng)堆和ACS堆中的進(jìn)行位置。當(dāng)處理溢出集合時(shí),這兩個(gè)變量告訴垃圾收集器應(yīng)該在什么位置停止。

    4.1.1.4 處理系統(tǒng)堆對(duì)象的標(biāo)識(shí)棧溢出

    在并行標(biāo)識(shí)(parallelMark)階段,如果線程已經(jīng)處理完了標(biāo)識(shí)棧,接下來需要檢查overflowSystemClasses和overflowACSClasses兩個(gè)變量是否被設(shè)置。如果其中某個(gè)變量被設(shè)置,那么這個(gè)線程就會(huì)試圖獲取對(duì)應(yīng)對(duì)象列表的控制權(quán),并且將這個(gè)變量設(shè)置為NULL。一旦線程獲取了控制權(quán),就會(huì)將引用壓棧到標(biāo)識(shí)棧,一定量的引用被壓棧之后,向本地標(biāo)識(shí)隊(duì)列發(fā)布任務(wù),以允許其他線程輔助進(jìn)行后續(xù)工作。

    如果在處理的過程中,再次發(fā)生了標(biāo)識(shí)棧溢出,線程會(huì)記錄在發(fā)生溢出之前處理到的位置,然后重復(fù)上面的工作。

    4.1.2 并行標(biāo)識(shí)

    由于優(yōu)化的按位清理算法和壓縮避免機(jī)制的存在,使得一個(gè)垃圾收集周期的主要時(shí)間消耗在對(duì)象標(biāo)識(shí)階段。所以,開發(fā)了并行標(biāo)識(shí)技術(shù),該技術(shù)使得在單CPU的主機(jī)上不降低標(biāo)識(shí)的性能,并且能夠在8路主機(jī)上將性能提高4倍左右。

    在標(biāo)識(shí)階段的時(shí)間主要消耗在一些輔助線程以及協(xié)調(diào)這些輔助線程共同工作上。一個(gè)線程作為主要線程,也就是我們所說的垃圾收集主線程。這個(gè)線程負(fù)責(zé)掃描C堆棧,找到活動(dòng)的根對(duì)象。在一個(gè)具有N個(gè)CPU的主機(jī)上,會(huì)創(chuàng)建N-1個(gè)輔助線程來輔助完成后續(xù)的標(biāo)識(shí)階段工作。輔助線程的數(shù)量可以由虛擬機(jī)啟動(dòng)參數(shù)

    -Xgcthreadsn來重新設(shè)定。設(shè)置為1表示沒有輔助線程,該參數(shù)取值范圍為1到N。

    更高層面講,每個(gè)標(biāo)識(shí)線程擁有自己的本地棧以及共享的隊(duì)列,這兩個(gè)變量都包含了那些已經(jīng)被標(biāo)識(shí)但是尚未被掃描的對(duì)象的引用。輔助線程大部分的標(biāo)識(shí)工作都是依賴本地的棧變量來完成的,只有在需要負(fù)載均衡的時(shí)候才會(huì)在共享隊(duì)列上進(jìn)行同步的操作。由于對(duì)于標(biāo)識(shí)比特位的操作是原子操作,所以無需獲取鎖。

    由于每個(gè)線程的棧都可以容納4KB個(gè)條目,標(biāo)識(shí)隊(duì)列可以容納2KB個(gè)條目,所以大大降低了標(biāo)識(shí)棧溢出的發(fā)生幾率。

    4.1.3 并發(fā)標(biāo)識(shí)

    并發(fā)標(biāo)識(shí)機(jī)制保證在堆內(nèi)存增大的時(shí)候,能夠降低垃圾收集的暫停時(shí)間。在堆滿之前,開始進(jìn)行并發(fā)標(biāo)識(shí):垃圾收集器通知每個(gè)線程掃描自己的執(zhí)行棧以查找根對(duì)象,然后基于這些根對(duì)象開始進(jìn)行并發(fā)的追蹤,追蹤是在進(jìn)行鎖堆分配的時(shí)候,由一個(gè)較低優(yōu)先級(jí)的后臺(tái)線程以及全部的應(yīng)用線程一起完成。

    由于垃圾收集器在應(yīng)用運(yùn)行的同時(shí)標(biāo)識(shí)活動(dòng)對(duì)象,所以必須記錄已經(jīng)追蹤的對(duì)象的任何變化。為了達(dá)到這個(gè)目的,他采用了一種寫隔離(write barrier)的技術(shù),在對(duì)象發(fā)生變化時(shí)被激活。首先將堆分隔成512字節(jié)的段,每個(gè)段對(duì)應(yīng)卡片表(card table)中的一個(gè)字節(jié)。當(dāng)指向一個(gè)對(duì)象的引用發(fā)生變化時(shí),對(duì)應(yīng)這個(gè)對(duì)象所在段的卡片表中的字節(jié)被設(shè)置為0x01。使用字節(jié)而不是比特位有兩個(gè)原因:字節(jié)的寫入速度比比特位更快,另外,字節(jié)中其他的比特位還可以用作其他用途。

    如果發(fā)生以下情況,就會(huì)開始STW(Stop The World)收集:

    · 分配失敗

    · System.gc()

    · 并發(fā)標(biāo)識(shí)階段完成

    垃圾收集器開始進(jìn)行并發(fā)標(biāo)識(shí)階段的工作,以試圖在堆耗盡之前完成垃圾收集工作。虛擬機(jī)啟動(dòng)參數(shù)可以管理并發(fā)標(biāo)識(shí)的時(shí)間。

    在STW階段,垃圾收集器掃描所有的根對(duì)象,并且通過標(biāo)識(shí)卡片來查看需要進(jìn)一步追蹤的對(duì)象,然后按照普通的模式進(jìn)行清理。這樣能夠保證在并發(fā)標(biāo)識(shí)階段開始時(shí)的不可到達(dá)對(duì)象都可以被清理,但是無法保證在并發(fā)標(biāo)識(shí)階段進(jìn)行過程中變成不可到達(dá)的對(duì)象也被清理。

    并發(fā)標(biāo)識(shí)策略可以減少垃圾收集的暫停時(shí)間,但是會(huì)帶來額外消耗,因?yàn)閼?yīng)用線程需要在獲取堆鎖的時(shí)候進(jìn)行一些跟蹤工作。具體的性能降低取決于有多少空閑的CPU時(shí)間可以給后臺(tái)線程使用。另外,寫隔離機(jī)制也會(huì)有額外的消耗。

    開啟并發(fā)標(biāo)識(shí)的參數(shù)為:

    -Xgcpolicy:<optthruput|optavgpause>

    設(shè)置-Xgcpolicy參數(shù)為optthruput禁用并發(fā)標(biāo)識(shí)。optthruput是默認(rèn)設(shè)置。如果你的應(yīng)用中不存在暫停時(shí)間帶來的問題,可以使用這個(gè)默認(rèn)選項(xiàng)獲得最好的吞吐能力。

    設(shè)置-Xgcpolicy參數(shù)為optavgpause啟用并發(fā)標(biāo)識(shí)。如果應(yīng)用因?yàn)槔占臅和r(shí)間導(dǎo)致響應(yīng)能力下降,可以使用這個(gè)參數(shù)來改善情況,但是會(huì)降低應(yīng)用吞吐量。

    4.2 清理階段

    標(biāo)識(shí)階段結(jié)束之后,標(biāo)識(shí)集合中對(duì)于堆中每個(gè)可到達(dá)對(duì)象都進(jìn)行了標(biāo)識(shí),而且是分配集合的子集。清理階段就是計(jì)算分配集合和標(biāo)識(shí)集合的差集,也就是說,找到那些已經(jīng)被分配但是已經(jīng)不被引用的對(duì)象。

    最初,使用按位清理(bitsweep)技術(shù),這種技術(shù)檢查標(biāo)識(shí)集合中較長(zhǎng)的連續(xù)0序列,可能對(duì)應(yīng)的就是空閑空間。一旦找到連續(xù)0序列,垃圾收集器檢查這個(gè)序列開始位置對(duì)應(yīng)的對(duì)象的大小,來計(jì)算可以釋放多少空間。如果這個(gè)大小大于512字節(jié)加上對(duì)象頭大小,那么這一段空間就會(huì)重新加入到空閑鏈表中去。

    未返回到空閑鏈表中的小的空閑空間,就被稱作是“暗物質(zhì)”,當(dāng)緊鄰“暗物質(zhì)”空間的對(duì)象被釋放之后,或者壓縮動(dòng)作執(zhí)行之后,他就可能再次返回到空閑鏈表中去。垃圾收集器不需要清理空閑段中的每個(gè)對(duì)象,因?yàn)檫@一段都是可以被清理的對(duì)象。在這個(gè)過程中,標(biāo)識(shí)集合會(huì)整個(gè)復(fù)制到分配集合中去,這樣分配集合就表示了堆中所有的已分配對(duì)象。

    4.2.1 并發(fā)按位清理技術(shù)

    并發(fā)按位清理技術(shù)能夠盡量使用可用的處理器以提高清理階段的執(zhí)行效率。在并發(fā)清理階段,垃圾收集器使用和并發(fā)標(biāo)識(shí)階段同樣的輔助線程,所以,默認(rèn)的參與并發(fā)處理的線程數(shù)也可以由虛擬機(jī)參數(shù)-Xgcghreadsn來設(shè)置。堆被分割成多個(gè)段,段的數(shù)量要遠(yuǎn)遠(yuǎn)大于并發(fā)清理線程的數(shù)量,計(jì)算公式如下:

    · 32 * 輔助線程數(shù)量 或者

    · 堆的最大大小/16MB

    中選擇較大的一個(gè)。每次一個(gè)輔助線程選擇一個(gè)段來掃描,然后按位清理,并且保存每個(gè)段的清理結(jié)果,完成之后,再重新建立空閑鏈表。

    4.3 壓縮階段

    在清理階段結(jié)束之后,垃圾收集器可以對(duì)堆中剩余的活動(dòng)對(duì)象進(jìn)行壓縮,以移除他們之間的空閑空間。這個(gè)壓縮過程是非常復(fù)雜的,因?yàn)橐坏┮苿?dòng)了一個(gè)對(duì)象,那么所有指向這個(gè)對(duì)象的引用都需要修改。如果是來自棧的引用,那么垃圾收集器還不能確定這就是一個(gè)對(duì)象引用(有可能碰巧只是一個(gè)浮點(diǎn)數(shù)),就不能移動(dòng)這個(gè)對(duì)象。這樣的對(duì)象還會(huì)繼續(xù)保持在原來的位置,并且dosed標(biāo)識(shí)位會(huì)被設(shè)置。類似的,JNI操作中引用的對(duì)象也是要固化在原來的位置不能被移動(dòng)的,直到JNI操作結(jié)束,不再引用這個(gè)對(duì)象的時(shí)候,才可能被移動(dòng)。利用mptr的低三位設(shè)置為0,垃圾收集器可以在兩個(gè)階段內(nèi)壓縮那些可以移動(dòng)的對(duì)象。其中一位用來標(biāo)識(shí)對(duì)象被清理,這個(gè)清理標(biāo)識(shí)位出現(xiàn)在兩個(gè)地方:size+flags(即OLINK_IsSwapped)以及mptr(即GC_FirstSwapped),這兩種情況中,最低的位(x01)都會(huì)被設(shè)置。

    下圖可以幫助你理解壓縮過程:

    image017

    圖表 10 壓縮階段工作

    圖表10展示了壓縮的效果。假設(shè)從A到B是一個(gè)走廊,走廊里面有一些家具(藍(lán)色的塊),代表對(duì)象。空白的區(qū)域代表空閑空間或者“暗物質(zhì)”,有兩個(gè)家具被固定在地板上(交叉紋理塊),代表固化的或者不能被移動(dòng)的對(duì)象。壓縮的過程就類似你要把家具從B推到A,盡可能的靠近A端。但是,不幸的是,你不能把家具舉起來越過固定在地板上的家具,所以,他們右側(cè)的家具最遠(yuǎn)也就推到緊鄰他們的位置。

     

    4.3.1 避免壓縮

    image019

    圖表 11 荒地

    避免壓縮主要是致力于為對(duì)象找到合適的放置位置,以減少或者避免移動(dòng)對(duì)象。避免緊技術(shù)中最主要的一個(gè)概念叫做荒地預(yù)留。荒地預(yù)留技術(shù)試圖在堆中預(yù)留一段空間,然后盡量在其他地方進(jìn)行對(duì)象分配。在堆頂和荒地部分之間,定義一個(gè)邊界。如果存在大對(duì)象的分配,或者上一次垃圾收集之后尚未滿足分配需求的情況下,就會(huì)使用荒地空間。

    圖表11是堆中荒地的示意圖。荒地在活動(dòng)的堆空間的最后進(jìn)行分配,初始大小是活動(dòng)堆空間的5%,根據(jù)實(shí)際需求進(jìn)行收縮或者擴(kuò)展。在鎖堆分配失敗的情況下,如果對(duì)象大小小于64KB或者上一次垃圾收集的結(jié)果取得足夠的進(jìn)展,那么就再開始一輪垃圾收集工作。足夠的進(jìn)展的意思是,自從上一次垃圾收集以來,至少有30%的堆空間被占用。30%是默認(rèn)值,可以通過-Xminf參數(shù)設(shè)置。如果沒有取得足夠進(jìn)展,或者對(duì)象大于等于64KB,那么會(huì)嘗試在荒地中進(jìn)行分配。這樣就能夠避免垃圾收集和堆壓縮動(dòng)作。

    如果未設(shè)置虛擬機(jī)參數(shù)-Xnocompactgc并且以下幾個(gè)條件任何一個(gè)為true,那么就會(huì)發(fā)生堆壓縮動(dòng)作:

    · 設(shè)置了虛擬機(jī)參數(shù)-Xcompactgc

    · 清理階段結(jié)束之后,還是無法滿足分配需求

    · 調(diào)用System.gc()并且在最后一次分配失敗發(fā)生或者并發(fā)標(biāo)識(shí)收集之前發(fā)生了壓縮動(dòng)作

    · TLH消耗了至少一半的存儲(chǔ),并且TLH的平均大小低于1000字節(jié)

    · 堆的空閑空間小于5%

    · 堆的空閑空間小于128KB

    4.3.2 增量壓縮
    4.3.2.1 介紹

    垃圾收集釋放了對(duì)象空間之后,在堆中就會(huì)產(chǎn)生碎片。那么會(huì)引發(fā)一種現(xiàn)象,堆中還有足夠的空閑空間,但是由于他們是不連續(xù)的,所以無法進(jìn)行后續(xù)的分配。

    壓縮動(dòng)作就是用來整理堆中的碎片,他將堆中分散的已分配的存儲(chǔ)段移動(dòng)到堆的一端,那么在堆的另一端就會(huì)生成一個(gè)較大的連續(xù)的空閑空間。但是壓縮會(huì)增加垃圾收集的暫停時(shí)間,對(duì)于1GB的堆,如果進(jìn)行了壓縮動(dòng)作,垃圾收集的暫停時(shí)間可能增加到40秒。對(duì)于應(yīng)用而言,這么長(zhǎng)的暫停時(shí)間通常是不可接受的。增量壓縮技術(shù)就是將壓縮動(dòng)作分散到多次垃圾收集周期中,以減少暫停時(shí)間。

    增量壓縮的另一個(gè)重要作用是清理暗物質(zhì)。暗物質(zhì)就是堆中的很小的(小于512字節(jié))存儲(chǔ)片,這些存儲(chǔ)片不在空閑鏈表中,因此不能被重新利用。暗物質(zhì)的存在程度直接影響了應(yīng)用的吞吐能力,因?yàn)樵蕉嗟陌滴镔|(zhì)就會(huì)導(dǎo)致堆中可用的存儲(chǔ)空間越少,可用存儲(chǔ)越少,垃圾收集發(fā)生的頻率就會(huì)越高,對(duì)于應(yīng)用的性能會(huì)產(chǎn)生非常明顯的影響。這些暗物質(zhì)分散在整個(gè)堆中,會(huì)占用堆的大部分空間。

    4.3.2.2 增量壓縮概覽

    image021

    圖表 12 增量壓縮

    在圖表12中,涂色的塊表示已經(jīng)分配的空間,未涂色的塊表示空閑空間。暗物質(zhì)會(huì)分布在空閑空間之間。下半部分表示在增量壓縮之后,對(duì)象被移動(dòng)到段的一側(cè),另一側(cè)就整理出了較大的連續(xù)空閑空間。

    只有在堆大小大于某個(gè)值(當(dāng)前是128MB)時(shí),才會(huì)發(fā)生增量壓縮。如果堆小于128MB,增量壓縮并不會(huì)帶來比完整壓縮更短的暫停時(shí)間。

    增量壓縮有如下兩步:

    1) 記錄和標(biāo)識(shí)指向壓縮區(qū)域的所有引用,這個(gè)動(dòng)作在標(biāo)識(shí)階段完成

    2) 計(jì)算對(duì)象的新位置,在區(qū)域內(nèi)進(jìn)行壓縮,并修改指向被移動(dòng)對(duì)象的引用

    增量壓縮在一個(gè)周期內(nèi)進(jìn)行,一個(gè)增量壓縮周期就是指在垃圾收集周期內(nèi),一次一個(gè)區(qū)域的完成整個(gè)堆的壓縮動(dòng)作。壓縮動(dòng)作會(huì)跨越多個(gè)垃圾收集周期,因此將壓縮動(dòng)作的耗時(shí)分散到多個(gè)垃圾收集周期,減少暫停隨時(shí)間。

    4.3.2.3 增量壓縮相關(guān)的主要參數(shù)

    默認(rèn)是啟用增量壓縮的,是否運(yùn)行增量壓縮取決于一些觸發(fā)條件。但是有兩個(gè)參數(shù)可以由用戶決定是使用增量壓縮還是傳統(tǒng)壓縮:

    · -Xpartialcompactgc,表示每次垃圾收集都使用增量壓縮,除非必須進(jìn)行完整的壓縮動(dòng)作

    · -Xnopartialcompactgc,表示禁用增量壓縮機(jī)制

    但是,需要提醒的是,-X參數(shù)屬于非標(biāo)準(zhǔn)的虛擬機(jī)參數(shù),可能在未通知的情況下進(jìn)行修改。

    4.4 引用對(duì)象

    引用對(duì)象能夠使得所有的對(duì)象引用都以同樣的方式被操作和處理,因此垃圾收集器在堆上創(chuàng)建兩個(gè)獨(dú)立的對(duì)象:一個(gè)是對(duì)象本身,一個(gè)是引用對(duì)象。當(dāng)對(duì)象處于不可到達(dá)狀態(tài)時(shí),該引用對(duì)象可以方便的加入到一個(gè)隊(duì)列中去。SoftReference,WeakReference以及PhantomReference由用戶創(chuàng)建,不能修改,即,他們只能指向創(chuàng)建時(shí)的那個(gè)對(duì)象,不能指向其他對(duì)象。帶有finalizer方法的對(duì)象在創(chuàng)建時(shí)注冊(cè)Finalizer類,因此FinalReference對(duì)象指向一個(gè)需要finalize的對(duì)象,并且關(guān)聯(lián)到Finalizer隊(duì)列。

    在垃圾收集階段,引用對(duì)象被特殊處理:在標(biāo)識(shí)階段,不會(huì)處理引用對(duì)象的引用字段,標(biāo)識(shí)階段結(jié)束之后,按照如下順序處理引用字段:

    1) Soft

    2) Weak

    3) Final

    4) Phantom

    對(duì)于SoftReference對(duì)象的處理稍有特殊,如果他所指向的對(duì)象未被標(biāo)識(shí),ST組件會(huì)清理這個(gè)SoftReference。如果存儲(chǔ)不足,那么垃圾收集器根據(jù)最近最常用的規(guī)則來進(jìn)行清理。使用率根據(jù)最后一次被調(diào)用get方法來測(cè)量。一旦一個(gè)引用對(duì)象被處理,他所指向的真實(shí)對(duì)象會(huì)被標(biāo)識(shí),這樣能夠確保如果一個(gè)FinalReference也指向同一個(gè)對(duì)象,他能夠看到這個(gè)標(biāo)識(shí)。FinalReferece不會(huì)被放到處理隊(duì)列中去。因此,在垃圾收集周期可以成功處理引用對(duì)象。

    指向未標(biāo)識(shí)對(duì)象的引用對(duì)象最初被放到ReferenceHandler線程的隊(duì)列中,ReferenceHandler線程把對(duì)象從他的隊(duì)列中移除,同時(shí)查看這個(gè)對(duì)象自己的隊(duì)列是否存在,如果存在,則這個(gè)對(duì)象被重新放入ReferenceHandler線程的隊(duì)列中去,以進(jìn)行后續(xù)的處理。因此,F(xiàn)inalReference會(huì)重新入隊(duì),確保最后finalize方法被finalizer線程執(zhí)行。

    4.4.1 JNI Weak引用

    JNI Weak引用對(duì)象提供類似WeakReference對(duì)象的功能,但是處理機(jī)制是不同的。一段JNI代碼可以創(chuàng)建或者刪除JNI Weak引用對(duì)象,這個(gè)對(duì)象指向一個(gè)真正的對(duì)象。當(dāng)引用對(duì)象指向的對(duì)象未被標(biāo)識(shí),垃圾收集器會(huì)清理這個(gè)引用對(duì)象,但是不同于前一章提到的隊(duì)列機(jī)制。需要注意的是,如果清理JNI Weak引用對(duì)象失敗,可能會(huì)引發(fā)內(nèi)存泄漏以及性能方面的問題。對(duì)于全局的JNI引用,也是同樣的處理方式。JNI Weak引用對(duì)象最后會(huì)被引用對(duì)象處理線程處理,因此,對(duì)于一個(gè)已經(jīng)被finalize的對(duì)象,如果存在指向他的phantom引用,他的JNI Weak引用對(duì)象也會(huì)持續(xù)存在。

    4.5 堆擴(kuò)展

    堆擴(kuò)展發(fā)生在垃圾收集完畢并且所有線程重新啟動(dòng)之后,但是HEAP_LOCK尚被持有的情況下。如果滿足以下條件之一,堆的活動(dòng)空間會(huì)進(jìn)行擴(kuò)展,直到堆的最大限制:

    · 垃圾收集無法釋放足夠的空間來滿足分配需求

    · 空閑空間低于最小空閑空閑設(shè)定,-Xminf參數(shù),默認(rèn)30%

    · 垃圾收集占用了超過13%的時(shí)間,并且按照最小擴(kuò)展量(-Xmine)擴(kuò)展之后,還是無法滿足堆內(nèi)最大空閑空間(-Xmaxf)要求的。

    每次擴(kuò)展的量計(jì)算規(guī)則如下:

    · 如果由于無法滿足堆的空閑空間為-Xminf(30%),垃圾收集器計(jì)算能夠滿足-Xminf堆空閑的擴(kuò)展量。如果這個(gè)計(jì)算結(jié)果大于最大擴(kuò)展量-Xmaxe(默認(rèn)為0,即沒有最大擴(kuò)展量限制),那么會(huì)采用-Xmaxe設(shè)定的數(shù)量。如果計(jì)算結(jié)果小于-Xmine(默認(rèn)為1MB),那么會(huì)按照-Xmine設(shè)定進(jìn)行擴(kuò)展

    · 如果由于垃圾收集之后仍然不能滿足分配需求,并且垃圾收集的時(shí)間占用總運(yùn)行時(shí)間的比例不超過13%,則按照實(shí)際的分配需求來擴(kuò)展

    · 如果由于其他原因觸發(fā)擴(kuò)展,則垃圾收集器計(jì)算能夠滿足17.5%堆空閑的擴(kuò)展量。類似上面,根據(jù)-Xmaxe-Xmine的設(shè)定進(jìn)行調(diào)整

    · 最終,如果垃圾收集未能釋放出滿足分配需求的空間,則堆的擴(kuò)展要保證至少滿足分配需求

    所有計(jì)算出的擴(kuò)展量,在32位架構(gòu)上是64KB的整倍,在64位架構(gòu)上是4MB的整倍。

    4.6 堆收縮

    堆收縮發(fā)生在垃圾收集完畢并且所有線程都處于掛起狀態(tài)時(shí)。如果滿足以下任何一個(gè)條件,就不會(huì)發(fā)生堆收縮:

    · 垃圾收集未能釋放滿足分配需求的空閑空間

    · -Xmaxf參數(shù)設(shè)定為100%(默認(rèn)是60%)

    · 在最近的3次垃圾收集之中發(fā)生過堆擴(kuò)展

    · 由于調(diào)用System.gc()發(fā)生的垃圾收集,并且在收集周期開始之前,堆的空閑空間小于-Xminf(30%)

    如果以上條件都不滿足,并且存在大于-Xmaxf的空閑空間,垃圾收集器會(huì)計(jì)算收縮量,以保證能夠擁有-Xmaxf的空閑空間,并且不小于初始大小(-Xms)。計(jì)算出的收縮量在32位架構(gòu)上是64KB的整倍,在64位架構(gòu)上是4MB的整倍。

    如果滿足以下任何一個(gè)條件,會(huì)在收縮之前發(fā)生壓縮:

    · 在本次垃圾收集周期未進(jìn)行壓縮

    · 堆尾沒有空閑存儲(chǔ)片,或者堆尾的空閑存儲(chǔ)片小于需要收縮量的10%

    · 在上一次垃圾收集周期中未發(fā)生壓縮和收縮

    4.7 Resettable JVM

    從1.3.0以后,引入了Resettable JVM,該虛擬機(jī)只能運(yùn)行在z/OS平臺(tái)。在http://www.s390.ibm.com/Java有詳細(xì)的文檔

    posted on 2008-04-22 11:29 YODA 閱讀(2182) 評(píng)論(0)  編輯  收藏

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲人成人无码网www国产| 亚洲不卡av不卡一区二区| caoporn国产精品免费| 亚洲国产精品第一区二区 | 免费人成视频在线观看视频| 丁香花在线观看免费观看图片 | 亚洲av永久无码精品表情包| 国产免费久久精品99re丫y| 少妇亚洲免费精品| 亚洲国产精品线观看不卡| 亚洲精品视频免费观看| 色影音免费色资源| 久久精品无码专区免费| 国产亚洲精品bv在线观看| 亚洲国产精品无码久久SM| 日韩精品无码人妻免费视频| 最近免费中文字幕MV在线视频3| 亚洲精品乱码久久久久久V| 午夜亚洲www湿好大| 免费一级毛片女人图片| 色影音免费色资源| 国产午夜精品久久久久免费视| 亚洲中文无码永久免| 亚洲视频一区网站| 亚洲色成人中文字幕网站| 国产免费直播在线观看视频| 99在线精品视频观看免费| 免费看一区二区三区四区| 免费无码国产在线观国内自拍中文字幕 | 亚洲免费人成在线视频观看| 人人狠狠综合久久亚洲| 亚洲人成影院午夜网站| 亚洲av日韩av高潮潮喷无码| 久久久久亚洲AV综合波多野结衣| 热久久精品免费视频| 69xx免费观看视频| 99re6在线精品视频免费播放| 国产成人无码精品久久久免费| 久久亚洲精品高潮综合色a片| 亚洲videosbestsex日本| 亚洲视频国产精品|