Java與C#的記憶體回收機制的詳情介紹

來源:互聯網
上載者:User
(一)記憶體回收行程的基本假定

(1)最近被分配記憶體空間的對象最有可能需要被釋放。在方法被執行前,通常需要為該方法所使用到的對象分配記憶體空間,搜尋最近被分配的對象集合有助於花費最少的工作來釋放進可能多的空閑記憶體空間。

(2)生命期最長的對象需要釋放的可能性最小。在通過幾輪記憶體回收後仍然存在的對象不大可能是那種能夠在下一輪迴收中被釋放的臨時對象,搜尋這些記憶體塊往往要進行大量的工作,卻只能釋放很小一部分的記憶體空間。

(3)同時分配記憶體的對象通常也會同時使用。將同時分配記憶體的Object Storage Service位置彼此相連有助於提高緩衝效能。

(二)幾種記憶體回收機制

(1)標記-清除收集器

  這種收集器首先遍曆對象圖並標記可到達的對象,然後掃描堆棧以尋找未標記對象並釋放它們的記憶體。這種收集器一般使用單線程工作並停止其他動作。

(2)標記-壓縮收集器

  有時也叫標記-清除-壓縮收集器,與標記-清除收集器有相同的標記階段。在第二階段,則把標記對象複製到堆棧的新域中以便壓縮堆棧。這種收集器也停止其他動作。

(3)複製收集器

  這種收集器將堆棧分為兩個域,常稱為半空間。每次僅使用一半的空間,jvm產生的新對象則放在另一半空間中。gc運行時,它把可到達對象複製到另一半空間,從而壓縮了堆棧。這種方法適用於短生存期的對象,持續複製長生存期的對象則導致效率降低。

(4)增量收集器

增量收集器把堆棧分為多個域,每次僅從一個域收集垃圾。這會造成較小的應用程式中斷。

(5)分代收集器

  這種收集器把堆棧分為兩個或多個域,用以存放不同壽命的對象。jvm產生的新對象一般放在其中的某個域中。過一段時間,繼續存在的對象將獲得使用期並轉入更長壽命的域中。分代收集器對不同的域使用不同的演算法以最佳化效能。

(6)並發收集器

  並發收集器與應用程式同時運行。這些收集器在某點上(比如壓縮時)一般都不得不停止其他動作以完成特定的任務,但是因為其他應用程式可進行其他的後台操作,所以中斷其他處理的實際時間大大降低。

(7)並行收集器

  並行收集器使用某種傳統的演算法並使用多線程並行的執行它們的工作。在多cpu機器上使用多線程技術可以顯著的提高java應用程式的可擴充性。

(三).NET架構記憶體回收機制

.NET架構套件含一個託管堆,所有的.NET語言在分配參考型別對象時都要使用它。像實值型別這樣的輕量級對象始終分配在棧中,但是所有的類執行個體和數組都被產生在一個記憶體池中,這個記憶體池就是託管堆。

.NET架構中的記憶體回收行程被稱為分代的記憶體回收行程(Generational Garbage Collector),也就是說被分配的對象劃分為3個類別,或稱為“代”。分別為0,1,2。0、1、2代對應的託管堆的初始化大小分別是256K,2M和10M。記憶體回收行程在發現改變大小能夠提高效能的話,會改變託管堆的大小。例如當應用程式初始化了許多小的對象,並且這些對象會被很快回收的話,記憶體回收行程就會將第0代的託管堆變為128K,並且提高回收的頻率。如果情況相反,記憶體回收行程發現在第0代的託管堆中不能回收很多空間時,就會增加託管堆的大小。在應用程式初始化的之前,所有等級的託管堆都是空的。當對象被初始化的時候,他們會按照初始化的先後順序被放入第0代的託管堆中。 

最近被分配記憶體空間的對象被放置於第0代,因為第0代很小,小到足以放進處理器的二級(L2)緩衝,所以第0代能夠為我們提供對其中對象的快速存取。經過一輪記憶體回收後,仍然保留在第0代中的對象被移進第1代中,再經過一輪垃圾記憶體回收後,仍然保留在第1代中的對象則被移進第2代中。第2代包含了生存期較長的對象,這些對象至少經過了兩輪迴收。

C#程式為一個對象分配記憶體時,託管堆幾乎可以立即返回新對象所需的記憶體,託管堆之所以能有這樣高效的記憶體配置效能是由於託管堆較為簡單的資料結構。託管堆類似於簡單的位元組數組,有一個指向第一個可用記憶體空間的指標。

在某塊被某對象所請求時,上述指標值就會返回給調用函數,而指標會重新調整至指向下一個可用的記憶體空間。分配一個託管記憶體塊只比遞增一個指標的值稍微複雜一點。這也是託管堆所最佳化的效能之一。在一個不需太多記憶體回收的應用程式中,託管堆的表現會優於傳統的堆。

由於這個線性記憶體配置方法的存在,在C#應用程式中同時分配的對象在託管堆上通常會被分配成彼此相鄰。著安排和傳統的堆記憶體配置完全不同,傳統的堆記憶體配置是基於記憶體塊大小的。例如,兩個同時分配的對象在堆上的位置可能相距很遠,從而降低了緩衝的效能。因此雖然記憶體配置很快,但在一些比較重要的程式中,第0代中的可用記憶體很有可能會徹底被消耗光。記住,第0代小到可以裝進L2緩衝區,並且沒有被使用的記憶體不會被自動釋放。當第0代中沒有可以分配的有效記憶體時,就會在第0代中觸發一輪記憶體回收,在這輪記憶體回收中將刪除所有不再被引用的對象,並將當前正在使用中的對象移至第1代。針對第0代的記憶體回收是最常見的回收類型,而且速度很快。在第0代的垃圾記憶體回收不能有效請求到充足的記憶體時,就啟動第1代的垃圾記憶體回收。第2代的垃圾記憶體回收要作為最後一種手段而使用,若且唯若第1代和第0代的垃圾記憶體回收不能被提供足夠記憶體時進行。如果各代都進行了記憶體回收後仍沒有可用的記憶體,就會引發一個OutOfMemeryException異常 。

(四)Java記憶體回收機制

執行Java程式時記憶體如何放置?《Java編程思想》一書中提到了六個地方:

(1)寄存器(Register)

(2)棧(Stack)

(3)堆(Heap):用來置放所有Java對象

(4)靜態儲存空間(Static storage):用來存放“程式執行期間”一直存在的資料。用statci修飾。

(5)常量儲存空間(Constant storage)

(6)Non-RAM儲存空間:我理解為磁碟儲存區,即非記憶體地區。

Sun HotSpot 1.4.1使用分代收集器,它把堆分為三個主要的域:新域、舊域以及永久域。Jvm產生的所有新對象放在新域中。一旦對象經曆了一定數量的垃圾收集迴圈後,便獲得使用期並進入舊域。在永久域中jvm則儲存class和method對象。就配置而言,永久域是一個獨立域並且不認為是堆的一部分。這樣看來,採用了HotSpot引擎技術的JVM應該採用了和.NET架構類似的記憶體回收機制-----分代記憶體回收方法。

下面介紹如何控制這些域的大小。可使用-Xms和-Xmx 控制整個堆的原始大小或最大值。

  下面的命令是把初始大小設定為128M:

java –Xms128m

  –Xmx256m為控制新域的大小,可使用-XX:NewRatio設定新域在堆中所佔的比例。

  下面的命令把整個堆設定成128m,新域比率設定成3,即新域與舊域比例為1:3,新域為堆的1/4或32M:

java –Xms128m –Xmx128m

–XX:NewRatio =3可使用-XX:NewSize和-XX:MaxNewsize設定新域的初始值和最大值。

  下面的命令把新域的初始值和最大值設定成64m:

java –Xms256m –Xmx256m –Xmn64m

  永久域預設大小為4m。運行程式時,jvm會調整永久域的大小以滿足需要。每次調整時,jvm會對堆進行一次完全的垃圾收集。

  使用-XX:MaxPerSize標誌來增加永久域搭大小。在WebLogic Server應用程式載入較多類時,經常需要增加永久域的最大值。當jvm載入類時,永久域中的對象急劇增加,從而使jvm不斷調整永久域大小。為了避免調整,可使用-XX:PerSize標誌設定初始值。

  下面把永久域初始值設定成32m,最大值設定成64m。

java -Xms512m -Xmx512m -Xmn128m -XX:PermSize=32m -XX:MaxPermSize=64m

  預設狀態下,HotSpot在新域中使用複製收集器。該域一般分為三個部分。第一部分為Eden,用於產生新的對象。另兩部分稱為救助空間,當Eden充滿時,收集器停止應用程式,把所有可到達對象複製到當前的from救助空間,一旦當前的from救助空間充滿,收集器則把可到達對象複製到當前的to救助空間。From和to救助空間互換角色。維持活動的對象將在救助空間不斷複製,直到它們獲得使用期並轉入舊域。使用-XX:SurvivorRatio可控制新域子空間的大小。

  同NewRation一樣,SurvivorRation規定某救助域與Eden空間的比值。比如,以下命令把新網域設定成64m,Eden佔32m,每個救助域各佔16m:

java -Xms256m -Xmx256m -Xmn64m -XX:SurvivorRation =2

  如前所述,預設狀態下HotSpot對新域使用複製收集器,對舊域使用標記-清除-壓縮收集器。在新域中使用複製收集器有很多意義,因為應用程式產生的大部分對象是短壽命的。理想狀態下,所有過渡對象在移出Eden空間時將被收集。如果能夠這樣的話,並且移出Eden空間的對象是長壽命的,那麼理論上可以立即把它們移進舊域,避免在救助空間反覆複製。但是,應用程式不能適合這種理想狀態,因為它們有一小部分中長壽命的對象。最好是保持這些中長壽命的對象並放在新域中,因為複製小部分的對象總比壓縮舊域廉價。為控制新域中對象的複製,可用-XX:TargetSurvivorRatio控制救助空間的比例(該值是設定救助空間的使用比例。如救助空間位1M,該值50表示可用500K)。該值是一個百分比,預設值是50。當較大的堆棧使用較低的sruvivorratio時,應增加該值到80至90,以更好利用救助空間。用-XX:maxtenuring threshold可控制上限。

  為放置所有的複製全部發生以及希望對象從eden擴充到舊域,可以把MaxTenuring Threshold設定成0。設定完成後,實際上就不再使用救助空間了,因此應把SurvivorRatio設成最大值以最大化Eden空間,設定如下:

java … -XX:MaxTenuringThreshold=0 –XX:SurvivorRatio=50000 …

後記:正如《Java本紀之Java虛擬機器的10年》所提到的那樣“最近的五年,就是(JVM)繼續最佳化的五年。繼續進行最佳化的方法有幾條路,一是研究新的採樣演算法。因為採樣關係到不同的最佳化策略,會對整體效能有比較大的影響。二是研究深度最佳化的方法。三是研究垃圾收集的演算法。垃圾收集會帶來程式短暫的停頓,這會帶來負面的使用者體驗。於是,如何提高垃圾收集的效率,減少延遲,出現了五花八門的演算法,比如漸進式收集、火車演算法等。”提高語言的執行速度和效率一直是設計開發人員所追求的目標,因此記憶體回收的演算法也會隨日月的遞增而發展。我想也沒有哪個面試官敢輕易問你談談C#或者Java的記憶體回收機制(至少我還沒碰到過),很多問題一旦討論的深入,足以寫成長篇大著。但刨根問底、追根溯源真的是一件很美的事情,孔子登東山而小魯,登泰山而小天下。

以上就是Java與C#的記憶體回收機制的詳情介紹的內容,更多相關內容請關注topic.alibabacloud.com(www.php.cn)!

  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.