第十節:代

來源:互聯網
上載者:User

標籤:

代是CLR記憶體回收的一種機制,它唯一的目的是提升應用程式的效能,一個基於代的記憶體回收行程做出了以下幾點假設。

  1. 對象越新,生存期越短。
  2. 對象越老,生存期越長。
  3. 回收堆得一部分,速度快於回收整個堆。

無數的研究證明,對於現今的大多數應用程式,這些假設都是成立的,而且這些假設影響了記憶體回收行程的實現方式。本節將解釋代的工作原理。

託管堆在初始化時,不包含任何對象,添加到堆得對象稱為第0代對象。簡單的說,第0代對象是那些新構造的對象,記憶體回收行程從未檢查過他們。展示了一個新啟動的應用程式,它分配了5個對象(從A到E)。過了一會,對象C和E變得不可達。

 

 

CLR初始化時,它會為第0代對象選擇一個預算容器,假定為256KB(實際容量可能有所不同)。所以,如果分配一個新對象造成第0代超過預算,就必須啟動一次記憶體回收。

假設對象A-E剛好佔用256K。對象F分配時,記憶體回收行程必須啟動。記憶體回收行程判定對象C和E為垃圾因此會壓縮(磁碟重組)對象D,使其與對象B相鄰,順便說一句,之所以第0代的預算容器是256K,是因為所有這些對象能裝入CPU的L2緩衝,使記憶體的壓縮能以非常快的速度完成。在記憶體回收中存活的對象被認為第1代對象。第1代對象已經經曆了記憶體回收行程的一次檢查 ,如下如所示:

 

一次記憶體回收後,第0代就不包含任何對象了。和前面一樣,新對象會分配到第0代中。如所示,應用程式繼續運行,並新分配了對象F到K,另外,隨著應用程式繼續運行,對象B、H、G變的不可達,他們的記憶體將在某一時刻回收,

 

現在假設分配新對象L會造成第0代超過其256K的預算。由於第0代達到了預算,所以必須啟動記憶體回收。開始一次記憶體回收時,記憶體回收行程必須決定檢查哪些代。前面說過,當CLR初始時,它為第0代對象選擇了一個預算。同樣的,它還必須為第1代選擇一個預算,假定為第1代選擇的預算為2MB。

開始一次記憶體回收時,記憶體回收行程還檢查第1代佔據了多少記憶體。在本例中,由於第1代佔據的記憶體遠少於2MB,所以記憶體回收行程只檢查第0代的對象,回顧了一下基於代的記憶體回收行程做出的假設,第一個假設是新建立的對象具有一個短的生命期。因此,第0代可能包含大量垃圾,所以對第0代的回收,可能回收比較多的記憶體。記憶體回收期會忽略第1代中的對象,從而加快記憶體回收的速度。

顯然,忽略第1代中的對象可增強記憶體回收行程的效能。然而,對效能有更大提振作用的是現在不必遍曆託管堆中的每個對象。如果一個根或者一個對象引用了老一代的某個對象。記憶體回收行程就可以忽略老對象的所有內部引用,從而能更快的速度構造好可達的對象圖。當然,老對象的欄位也有可能引用新對象。為了確保對老對象中的這些已更新的欄位進行檢查,記憶體回收行程利用了JIT編譯器內部的一個機制。這個機制在對象的引用欄位發生變化時,會更新一個位標誌,這樣一來,記憶體回收期就知道從上一次記憶體回收之後,哪些老對象已被寫入。只有欄位發生變化的老對象才需檢查是否引用了第0代中的任何新對象。

 

基於代的記憶體回收行程還假設活的比較久的對象能繼續活下去。也就是說,第1代對象在應用程式中很有可能是繼續可達的。如果記憶體回收行程檢查第1代的對象很有可能找不到多少垃圾,結果是回收不了多少記憶體。因此,對第1代進行記憶體回收很可能是浪費時間,如果真有垃圾在第1代,它將呆在那裡。

所有倖存下來的第0代對象被提升為第1代的一部分。由於記憶體回收行程沒有檢查第1代,所以對象B的記憶體並沒有回收,及時它在上一次記憶體回收時已經不可達。同樣,在一次記憶體回收後,第0代對象不包含任何對象,等著分配新對象。假定應用程式繼續運行,並指派至L到對象O,另外,在運行過程中,應用程式停止使用對象G,L和M,使他們變的不可達,

 

 

假定指派至P導致第0代對象超過預算,記憶體回收發生。由於第1代對象佔據的記憶體仍小於2MB,所以記憶體回收期再次決定只回收第0代,忽略第1代中不可達的對象。

回收後,堆得情況如下:

 

第一代對象正在緩緩增長。假定第1代的增長導致它的所有對象佔據了2MB記憶體。這時,應用程式繼續運行,並指派至P到S,使第0代對象達到它的預算容量。這時堆如所示:

應用程式試圖指派至T時,由於第0代已滿,所以必須開始記憶體回收,但是,這一次記憶體回收發現,第1代佔據了太多記憶體,以至於達到第1代2Mb的預算。由於前幾次對第0代進行回收時,第1代已經有很多個物件變的不可達,所以這一次記憶體回收決定檢查第1代和第0代所有對象,兩次都被回收之後,堆得情況如下:

所示:

 

像前面一樣,記憶體回收後,第0代的倖存者被提升至第1代,第1代的倖存者被提升到第2代,第0代再次空出來,準備迎接新對象的到來。第二次的對象已經經過了至少2次或更多次的檢查。雖然到目前為止已經發生過多次記憶體回收,但只有在第1代達到它的預算時,才會檢查第1代中的對象。而在此之前,一般都對第0代進行了好幾次記憶體回收。

CLR的託管堆只支援3代:第0代、第1代、第2代。沒有第3代。CLR初始化時,會為每一代選擇預算。如前所示,第0代的預算約為256K,第1代約為2Mb,D第2代約為10mb,同樣的。預算的大小以提升效能為宜,預算越大,記憶體回收的頻率越低。再次提醒你,效能的提升源於最開始的假設:新對象的生存期較短,老對象傾向於活的久一些。CLR的記憶體回收期是自調節的。這意味著記憶體回收期會在執行記憶體回收的過程中瞭解應用程式的行為。例如,假定應用程式構造了許多個物件,但每個對象用的時間很短,在這種情況下,對第0代的記憶體回收會回收大量記憶體。實際上,第0代的所有對象都可能被回收。

如果記憶體回收期發現在回收0代後存活下來的對象很少,就可能將第0代的預算從256KB降低128kB,已指派空間的減少,意味著記憶體回收將更頻繁的發生,但記憶體回收行程需要多的工作會減少,從而減少進程的工作集。實際上,如果第0代的對象都是垃圾,記憶體回收期就不必壓縮任何記憶體,只需要讓指標(NextObjPtr)指回第0代的起始處即可。

另一方面,如果記憶體回收行程回收了第0代,發現還有很多個物件存活,沒有多少記憶體被回收,就會增大第0代的預算,比如增大到521kb,現在記憶體回收的次數就減少了,但每次進行回收時,回收的記憶體要多得多。如果沒有回收到足夠的記憶體,記憶體回收行程就會執行一次完整記憶體回收。如果還不夠,就跑出OutofMemoryException異常。

到目前為止,我只討論了記憶體回收之後如何動態調整第0代的預算。但是,記憶體回收行程還會用類似的啟發學習法的演算法調整第1代和第2代的預算,這些代被記憶體回收時,記憶體回收行程會檢查有多少記憶體被回收,以及有多少對象倖存,基於這些結果記憶體回收行程可能要增大或減少這些代的預算。從而提升應用程式的整體效能。最終的結果是,記憶體回收行程會根據應用程式要求的記憶體負載來自動最佳化。

 

第十節:代

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.