垃圾回收機制

 

在介紹GC前,有必要對.net中CLR管理內存區域作簡要介紹:java

  一、 堆棧:用於分配值類型實例。堆棧主要操做系統管理,而不受垃圾收集器的控制,當值類型實例所在方法結束時,其存儲單位自動釋放。棧的執行效率高,但存儲容量有限。算法

  2 、GC堆:用於分配小對象實例。若是引用類型對象實例的大小小於85000字節,實例將被配置在GC堆上,當有內存分配或者回收時,垃圾收集器可能會對GC堆進行壓縮。數據庫

  三、 LOH:large object heap,用於分配大對象實例。若是引用類型對象的實例的大小不小於85000字節時,該實例將被分配到LOH堆上,而LOH堆不會被壓縮,並且只在徹底GC回收時被回收。c#

 

在面向對象的環境中,每一個類型都表明可供程序使用的一種資源,要使用這些資源,必須爲表明資源的類型分配內存,如下是訪問一個資源所需的步驟:緩存

1 調用IL指令newobj,爲表明資源的類型分配內存(通常使用c#new操做符來完成);性能優化

2 初始化內存,設置資源的初始化狀態並使資源可用。類型的實例構造器負責設置初始化狀態;服務器

3 訪問類型的成員來使用資源(有必要可用重複);數據結構

4 摧毀資源的狀態以進行清理;(大多數時候不須要,通常只有包含了本機資源—文件、套接字、數據庫鏈接等的類型才須要特殊清理,dispose或finalize)多線程

5 釋放內存。垃圾回收器獨自負責這一步。併發

 

NextObjPtr:該指針指向下一個對象在堆中的分配位置;剛開始時,NextObjPtr設爲地址空間區域的基地址。

 

C#的new操做符致使CLR執行如下步驟:

1 計算類型的字段所需的字節數;

2 加上對象的開銷所需的字節數。每一個對象都有兩個開銷字段:類型對象指針和同步快索引。對應32位應用程序,這兩個字段各自須要32位,因此每一個對象須要增長8個字節。對應64位應用程序,兩個字段各自須要64位,因此每一個對象須要增長16字節。

3 CLR檢查區域中是否有分配對象所需的字節數。若是託管堆有足夠的可用空間,就在NextObjPtr指針指向地址處放入對象,爲對象分配的字節會被清零。接着調用類型的構造器(爲this參數傳遞NextObjPtr),new操做符返回對象引用。就在返回這個引用以前,NextObjPtr指針的值會加上對象佔用的字節數來獲得一個新值,即下個對象放入托管堆時的地址。

 

局部性原理:cpu訪問存儲器時,不管存取指令仍是存取數據,所訪問的存儲單元都趨於彙集在一個較小的連續區域中。

對於託管堆,分配對象只須要在指針上加一個值,速度至關快。在許多應用程序中,差很少同時分配的對象彼此間有較強的聯繫,並且常常差很少在同一時間訪問。因爲託管堆在內存中連續分配這些對象,因此會由於引用的「局部化」locality而得到性能上的提高。具體來講,這意味着進程的工做集會很是小,應用程序只需使用不多的內存,從而提升了速度。還意味着代碼使用的對象能夠所有駐留在CPU的緩存中,結果是應用程序能以驚人的速度訪問這些對象,由於cpu在只需大多數操做時,不會由於「緩存未命中」cache miss而被迫訪問較慢的RAM。

 

判斷對象是否存活

  既然要清理垃圾,那麼必然要明白什麼是垃圾吧,垃圾的理解:一個對象成爲「垃圾」表示該對象不被任何其餘對象所引用。所以GC必須採用必定的算法在託管堆中遍歷全部對象,最終造成一個可達對象圖,而不可達的對象將成爲被釋放的垃圾對象等待收集。

  在明白了什麼是垃圾後,確定會對GC如何回收垃圾提出疑問。.net平臺下,每一個應用程序都有一組根(指針),它指向託管堆中的存儲位置,由JIT編譯器和CLR運行時維護根指針列表,主要包括全局變量、靜態變量、局部變量和寄存器指針等。GC正是經過根指針列表來得到託管堆中的對象圖,其中定義了應用程序根引用的託管堆中的對象,當GC啓動時,它假設全部對象都是可回收的垃圾,開始遍歷全部的根,將根引用的對象標記爲可達對象添加到可達對象圖中,在遍歷過程當中,若是根引用的對象還引用着其餘對象,則該對象也被添加到可達對象圖中,依次類推,GC經過根列表的遞歸遍歷,將能找到全部可達對象,並造成一個可達對象圖。同時那些不可達對象則被認爲是可回收對象,GC接着運行垃圾收集進程來釋放垃圾對象的內存空間。這種收集算法稱爲:標記和清除收集算法。

 

引用計數算法:

對象生存期的管理,有的系統採用的是某種引用計數算法。在這類系統中,堆上的每一個對象都維護這一個內存字段來統計程序中多少「部分」正在使用對象,隨着每個「部分」到達代碼中某個再也不須要對象的地方,就遞減對象的計數字段,直至計數字段變爲0,對象就能夠從內存中刪除了。引用計數算法最大的問題是處理很差循環引用。

引用跟蹤算法:

鑑於引用計數算法存在的問題,CLR改成使用一種引用跟蹤算法。引用跟蹤算法只關心引用類型的變量,由於只有這種變量才能引用堆上的對象;值類型變量直接包含值類型實例。引用類型變量可在許多場合使用,包括類的靜態和實例字段,或者方法的參數和局部變量。咱們將全部引用類型的變量都成爲根。CLR開始GC時,首先暫停進程中的全部線程,這樣能夠防止線程在CLR檢查期間訪問對象並更改其狀態。而後CLR進入GC標記階段,其中,CLR遍歷堆中全部的對象,將同步塊索引字段中的一位設爲0,代表全部對象都應刪除。而後CLR檢查全部的活動根,查看它們引用了哪些對象。若是一個根包含null,CLR忽略這個根並繼續檢查下個根。任何根若是引用了堆上的對象,CLR都會標記那個對象,也就是將該對象的同步塊索引中的位設爲1.一個對象被標記後,CLR會檢查那個對象中的根,標記它們引用的對象。若是發現對象已經標記,就不從新檢查對象的字段,這就避免了由於循環引用而產生的死循環了。

檢查完畢後,堆中的對象要麼已標記,要麼未標記。已標記的對象不能被垃圾回收,引用這說明至少有一個對象在引用它,該對象時可達的reachable。CLR在檢查完畢後,進入GC壓縮compact階段。壓縮倖存的對象,使他們佔用聯繫的內存空間。好處有:1 倖存對象在內存中緊挨着,恢復了引用的「局部化」,減少了應用程序的工做集,提高訪問這些對象時的性能;2 壓縮也可形成可用空間也所有是連續的,這段地址空間獲得解放,容許其餘對象入住,意味着壓縮託管堆也解決了本機(原生)堆的空間碎片化問題。壓縮後,引用倖存對象的根如今引用的仍是對象最初在內存中的位置,因此CLR還要從每一個根減去所引用的對象在內存中偏移的字節數,這樣就能保證每一個根的引用仍是和以前同樣的對象,只是對象在內存中變換了位置。

靜態字段引用的對象一直存在,直到用於加載類型的AppDomain卸載爲止。內存泄露的一個常見緣由是讓靜態字段引用某個集合對象,而後不停地向集合添加數據項。靜態字段使集合對象一直存活,而集合對象使全部數據項一直存活。所以,儘可能避免使用靜態字段。

CLR的GC是基於代的垃圾回收器,對於大多數應用程序:

對象越新,生存期越短;

對象越老,生存期越長;

回收堆的一部分,速度快於回收整個堆。

添加到堆的新對象會分配到第0代中,若是超過預算,就必須啓動一次垃圾回收,壓縮倖存的對象成爲第1代對象,一次垃圾回收後,第0代就不包含任何對象了。若是再有新對象,仍是會分配到第0代中。當系統在分配新對象時若是第0代超出預算,形成必須啓動垃圾回收。開始垃圾回收時,GC必須決定檢查哪些代,CLR初始化時會爲第0代對象選擇預算,事實上,它還必須爲第1代選擇預算。開始垃圾回收時,根據越新的對象活的越短。所以第0代包含更多的垃圾,能回收更多的內存,CLR通過計算,若是第1代佔用的內存遠少於預算,GC只檢查第0代的對象,忽略第1代中的對象,加快了垃圾回收速度,提高GC的性能,但對性能更大的提高做用是沒必要遍歷託管堆中的每一個對象,若是根或者對象引用了老一代的某個對象,GC就能夠忽略老對象內部的全部引用,能在更短的時間內構造號可達對象圖。固然老對象的字段也有可能引用新對象,爲了確保對老對象的已更新字段進行檢查,GC利用了JIT編譯器內部的一個機制,該機制在對象的引用字段發生變化是,會設置一個對應的位標誌,這樣,GC就知道自上一次垃圾回收以來,那行老對象已被寫入,只有字段發生變化的老對象才需檢查是否引用了第0代中的任何新對象。

在通過一次次的垃圾回收後,第0代的倖存者提高爲第1代,第1代的倖存者提高至第2代。有可能雖然在屢次垃圾回收後,但只有第1代超出預算時纔會檢查第1代中的對象。託管堆只支持三代。CLR初始化時,會爲每一代選擇預算,然而,CLR的垃圾回收器是自調節的。若是GC發現回收0代後存活下來的對象不多,就可能減少第0代的預算,分配空間減小意味着垃圾回收將更頻繁的發生,但GC每次作的事情也減小了,減少了進程的工做集。

CLR將大小爲85000字節或更大的對象稱爲大對象,它不是在小對象的地址空間分配,而是在進程地址空間的其餘地方分配。目前版本的GC不壓縮大對象,由於在內存中移動它們代價太高。大對象老是第2代。因此只能爲須要長時間存活的資源建立大對象。分配短期存活的大對象會致使第2代被更頻繁的回收,損害性能。

垃圾收集器將託管堆中對象分爲三代:0、1和2,在CLR初始化時,會選擇爲三代設置不一樣的闕值容量,通常爲:第0代大約爲256KB,第1代2MB,第2代10MB。容量越大效率越低,而GC收集器會自動調節其闕值容量來提高執行效率。在CLR初始化後,首先添加到託管堆中的對象都被定位第0代對象,當有垃圾回收執行時,未被回收的對象代齡將提高一級,變成第1代對象,然後新建對象仍未第0代對象。代齡越小表示對象越新,一般狀況下其生命週期也最短,所以GC老是先收集第0代的不可達對象內存。

隨着對象的不斷建立,垃圾收集再次啓動時則只會檢查0代對象並回收0代垃圾對象。而1代對象因爲未達到1代容量闕值,則不會進行垃圾回收操做,從而有效地提升了垃圾收集的效率,而這也是代齡機制在垃圾回收中的性能優化做用。當第0代對象釋放的內存不足以建立新的對象,同時1代對象的體積也超出了容量闕值是,垃圾收集器將同時對0代和1代對象進行垃圾回收。回收以後,未被回收的1代對象變化2級對象,未被回收的0代對象升級爲1代對象,然後新建的對象仍爲第0代對象。

注:微軟強烈建議不要經過GC.Collect方法來強制執行垃圾收集,這樣會妨礙GC自己的工做方式,經過Collect會使對象代齡不斷提高,擾亂應用程序的內存使用。只有在明確知道有大量對象中止引用時,才考慮使用GC.Collect方法來調用收集器。

何時進行垃圾回收

垃圾回收通常在下列狀況下進行:

1 內存不足溢出時,更確切的應該說是第0代對象充滿時。

2 調用GC.Collect方法強制執行垃圾回收。(通常不要執行此方法)

3 Windows報告內存不足時,CLR將強制執行垃圾回收。

4 CLR卸載AppDomain時,GC將對全部代齡的對象執行垃圾回收。

5 CLR正在關閉。CLR在進程正常終止時關閉。關閉期間,CLR任務進程中一切都不是根。對象有機會進行資源清理,但CLR不會試圖壓縮或釋放內存。整個進程都要終止了,Windows將回收進程的所有內存。

6 其餘狀況,如物理內存不足,超出短時間存活代的內存段門限,運行主機拒絕分配內存等。

 

垃圾回收模式

CLR啓動時會選擇一個GC模式,進程終止前該模式不會改變。兩種模式:

工做站:

該模式針對客戶端應用程序優化GC。GC形成的延時很低,應用程序線程掛起的時間很短,避免使用戶感到焦慮。在該模式中,GC假定機器上運行的其餘應用程序都不會消耗太多的CPU資源。

服務器:

該模式針對服務器端應用程序優化GC。被優化的主要是吞吐量和資源利用。GC假定當前應用程序是服務器上惟一的應用程序,該模式會致使託管堆被分隔爲多個部分,每個CPU一份,而且這些部分是能夠並行執行的。

默認狀況下應用程序運行在工做站模式,而且支持同時收集(Asp.net和Sqlserver 默認採用服務器模式),若是服務器應用程序運行在單處理上,那麼GC會採用工做站模式而且不支持同時收集。

 

GC還支持兩種子模式:併發(默認)和非併發。在併發方式中,GC有一個額外的後臺線程,在應用程序運行期間併發的標記對象,當由於分配對象形成第0代超出運算時,GC掛起全部線程,若是判斷回收0代或1代,GC如常進行。若是須要回收第2代,GC會增大第0代的大小(超過其運算)。在應用程序運行期間,GC運行一個普通優先級的後臺線程查找不可達對象,找到以後,GC再次掛起全部線程時,判斷是否要壓縮(移動)內存。如決定壓縮,內存會進行壓縮,根引用會被修正,而後應用程序恢復運行。這次GC花費時間比日常少,由於不可達對象集合已構造好。但GC也可能決定不壓縮內存,實際上,GC更傾向於選擇不壓縮。可用內存多,GC不壓縮,這樣有利於加強性能,但會增大應用程序的工做集。使用併發GC,應用程序消耗的內存一般比使用非併發GC的多。

 

GC模式是針對進程進行配置的,進程運行期間不能更改。但應用程序可使用GCSettings類的GCLatencyMode屬性對GC進行某種程度的控制。

LowLatency模式通常用它執行一次短時間的、時間敏感的操做,再講模式設置成普通的Batch或Interactive。LowLatency期間,GC會全力避免回收任何第2代對象,由於那樣花費的時間比較多。固然調用GC.Collect()或Windows告訴CLR系統內存低時,仍會回收第2代。該模式中,應用程序拋出OutOfMemoryException的概率大些,所以該模式的時間應儘可能短,避免分配太多對象,避免分配大對象。

GCLatencyMode oldMode = GCSettings.LatencyMode;

            System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();

            try

            {

                GCSettings.LatencyMode = GCLatencyMode.LowLatency;

                //run code

            }

            finally

            {

                GCSettings.LatencyMode = oldMode;

            }
View Code

終結的內部工做原理:

當包含本機資源的對象被GC時,GC會回收對象在託管堆上的內存,可是這會形成本機內存資源的泄露,是不容許的。若是須要清除非託管的包含本機資源的對象,CLR提供了終結機制,容許非託管對象在CLR斷定爲不可達、被斷定爲垃圾而且進行回收以前,執行終結機制,調用回收機制本身終結本身,釋放本機資源。

終極基類System.Object定義一個虛擬方法finalize,容許對象在被CLR斷定爲垃圾時,調用對象的Finalize方法。

應用程序建立新對象,new操做符會從堆中分配內存。若是對象定義了Finalize方法,那麼該類型的實例構造器在被調用以前,會將指向該對象的指針存放到一個終結列表finalization list中,終結列表是GC控制的一個內部的數據結構,列表中每一項都指向一個對象,回收該對象的內存前應調用它的Finalize方法。當對象進行GC時,若是GC認爲終結對象是垃圾時,會將對象從終結列表中移出,同時移入到freachable隊列中,freachable隊列也是GC的一種內部數據結構,隊列中的每一個引用都表明其Finalize方法已準備好調用的一個對象。一個特殊的高優先級的CLR線程專門調用Finalize方法,可避免潛在的線程同步問題,因爲該線程的特殊性,finalize中不要訪問線程的本地存儲。

當一個對象不可達時,GC把它視爲垃圾,該對象從終結列表移至freachable隊列中,對象再也不認爲是垃圾,不能回收它的內存。GC標記freachable對象是,將遞歸標記對象中的引用類型所引用的對象,因此這些對象也必須復活以便在回收過程當中存活,以後GC才結束垃圾標識,此過程當中,一些本來認爲是垃圾的對象復活了,而後GC壓縮移動可回收內存,將復活的對象提高到老的一代。如今,特殊的終結現場清空freachable隊列,執行每一個對象的finalize方法。下一次對老一代進行垃圾回收時,會發現已終結的對象成爲真正的垃圾,由於沒有應用程序指向它們,freachable隊列也不在指向它們。因此這些對象的內存會直接回收。整個過程,可終結對象須要執行兩次垃圾回收才能是否佔用的內存。在實際應用中,因爲對象可能被提高至另外一代,因此可能不止進行兩次垃圾回收。

對於非託管資源,須要開發者手動清理,方法主要有:Finalize方法和Dispose方法。

Finalize:

Finalize方法又稱爲終止化操做:經過對自定義類型實現一個Finalize方法來釋放非託管資源,而終止化操做在對象的內存回收以前經過調用Finalize方法來釋放資源。在析構函數中重寫Finalize方法,當垃圾管理器啓動時,對於斷定爲可回收的垃圾對象,GC會自動執行其Finalize方法清理非託管資源。

protected override void Finalize()

        {

            try

            {

                //執行自定義資源清理操做

            }

            finally

            {

                base.Finalize();

            }

        }
View Code

Finalize的缺點是:

終止化操做的時間沒法控制,執行順序也不能保證。

Finalize方法會極大的損失性能,GC使用一個終止話隊列的內部結構來跟蹤具備Finalize方法的對象。

重寫finalize方法的類型對象,其引用類型對象的代齡將被提高,帶來內存壓力。

 

Dispose:

Dispose模式的實現是:定義的類型必須實現System.IDisposable接口,該接口中定義了一個公有無參數的Dispose方法,程序設計者能夠在Dispose方法中實現對非託管資源的清理工做。

下面編寫一個項目中遇到使用Dispose方法的例子,功能是在套接字使用完畢後釋放資源

public class SocketConnection : IDisposable

    {

        //邏輯操做

        //.....................

 

        //實現Dispose

        public void Dispose()

        {

            try

            {

                this.ClientSock.Shutdown(SocketShutdown.Both);

                this.ClientSock.Close();

                this.Server = null;

            }

            catch (Exception ex)

            { }

        }

    }
View Code

 

總結:

在.net中,在堆棧上分配的資源在調用結束後,其內存自動會釋放。

託管堆中的資源,由CLR的垃圾管理器進行清理操做。

對於非託管資源,必須由程序設計者進行操做,而對於Finalize和Dispose,最好採用Dispose方法。

 

Java引用

Java1.2以前引用的定義:若是reference類型的數據中存儲的數值表明的是另一塊內存的起始地址,就稱這塊內存表明着一個引用。

Java1.2以後,強引用strong reference、軟引用soft reference、弱引用weak reference、虛引用phantom reference。

強引用:相似Object obj = new Object(),只要強引用還存在,GC永遠不會回收掉被引用的對象。

軟引用:描述一些還有用但並不是必需的對象。對應軟引用關聯着的對象,在系統將要發生內存溢出異常以前,將會把這些對象列進回收範圍之中進行第二次回收。若是此次回收尚未足夠的內存,纔會拋出內存溢出異常。

弱引用用來描述非必須的對象的,它的強度比軟引用更弱一些,倍弱引用關聯的對象只能生存到下次GC發生以前。

虛引用稱爲幽靈引用或者幻影引用,是最弱的一種引用關係。一個對象是否有虛引用的存在,徹底不會對其生存時間構成影響,也不發經過虛引用來取得一個對象實例,惟一目的是能在這個對象被GC時收到一個系統通知。

永久代的垃圾收集主要回收兩部份內容:廢棄常量和無用的類。

 

垃圾收集算法:

標記-清除算法Mark-Sweep:

問題有:效率問題,標記和清除的效率都不高。

空間問題,標記清除後會產生大量不連續的內存碎片,致使之後程序須要分配大對象時,沒法找到足夠連續的內存而不得不提起觸發另外一次GC。

 

複製算法Copying:

將內存分爲大小相等的兩塊,每次只使用 其中一塊,用完時將存活的對象複製到另一塊上,再把已使用的內存空間一次清理掉。代價是:內存縮小了原來的一半。現代商業的通常作法是分配一塊較大的Eden空間和兩塊較小的Survivor空間。每次使用Eden和其中一塊Survivor。HotSpot默認比例爲8:1。

 

標記-整理算法:過程與標記-清除算法同樣,但後續步驟不是直接對可回收對象進行整理,而是讓全部存活對象都向一端移動。

 

分代收集算法Generational Collection:

將java堆分爲新生代和老年代,根據各代的特色採用適當的收集算法。新生代中,每次都有大批對象死去,選用複製算法,只須要付出少許存活對象的複製成本就能夠完成收集。老年代對象存活率高、沒有額外空間對它進行分配擔保,採用標記-清理或者標記-整理算法。

 

收集器

在討論垃圾收集器的上下文語境中:

並行parallel:指多條垃圾收集線程並行工做,但此時用戶線程仍然處於等待狀態。

併發Concurrent:指用戶線程與垃圾收集線程同時執行(但不必定是並行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集程序運行於另外一個CPU上。

 

Serial收集器:

最基本、發展歷史最悠久的收集器,它是一個單線程收集器。單線程的意義並不只僅說明它只會使用一個CPU或一條收集線程去完成垃圾收集工做,更重要的是在進行GC時,必須暫停其餘全部的工做線程,直到它收集結束。它依然是虛擬機運行在client模式下的默認新生代收集器。簡單而高效。

 

ParNew收集器:

其實就是Serial收集器的多線程版本。它是許多運行在server模式下的虛擬機中首選的新生代收集器,其中一個與性能無關的重要緣由是,除了serial收集器外,目前只有它能與CMS收集器配合工做。

 

Parallel Scavenge收集器:

特色是關注點不同,它關注點是達到一個可控制的吞吐量。吞吐量值cpu用於運行用戶代碼的時間與cpu總消耗時間的比值。停頓時間越短越適合須要與用戶交互的程序,提高用戶體驗,而高吞吐量則能夠高效率地利用cpu時間,儘快完成程序的運算任務,主要適合在後臺運算而不須要太多交互的任務。

 

Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和「標記-整理」算法。吞吐量優先。在注重吞吐量以及CPU資源敏感的場合,均可以優先考慮Parallel Scavenge+Parallel Old收集器。

 

CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。

整個過程分爲4個步驟:

初始標記、併發標記、從新標記、併發清除,其中初始標記、從新標記這兩個步驟仍須要「stop the world」。初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,併發標記階段就是進行GC Roots Tracing的過程,從新標記階段則是爲了修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長一些,但遠比並發標記的時間短。因爲整個過程當中耗時最長的併發標記和併發清除過程收集器均可以與用戶線程一塊兒工做,因此整體來講,CMS收集器的內存回收過程是與用戶現場一塊兒併發執行的。

CMS的缺點:

CMS收集器對CPU資源很是敏感。併發階段,雖然不會致使用戶的線程停頓,可是由於佔用了一部分線程(或者說CPU資源)而致使應用程序變慢,總吞吐量會下降。

CMS收集器沒法處理浮動垃圾,可能出現Concurrent Mode Failure失敗而致使另外一次FULL GC的產生。

CMS收集器的標記-清除算法,會有大量空間碎片產生,空間碎片過多時,可能會給大對象分配帶來很大的麻煩。

 

G1收集器

G1 Garbage-First收集器是一款面向服務端的垃圾收集器。

有以下特色:

並行與併發

分代收集

空間整合:總體來看是基於標記-整理,從局部來看是具備複製算法實現的。

可預測的停頓

使用G1收集器時,它將整個java堆劃分爲多個大小相等的獨立區域,雖然還保留有新生代和老年代的概念,但已再也不是物理隔離的了,它們都是一部分Region的集合。之因此是可預測的,是由於它能夠有計劃的避免在整個java堆上進行全區域的垃圾收集,G1跟蹤各個Region裏面的垃圾堆積的價值大小,在後臺維護一個優秀列表,每次根據容許的收集時間,優先回收價值最大的Region,這種使用Region劃份內存空間以及有優先級的區域回收方式,保證了G1收集器在有限的時間內能夠獲取儘量高的收集效率。

G1收集器的步驟:

初始標記-併發標記-最終標記-篩選回收。

 

Java內存分配與回收策略:

對象優先在Eden分配

大對象直接進入老年代

長期存活的對象將進入老年代

相關文章
相關標籤/搜索