Java進階10 記憶體管理與垃圾回收
整個教程中已經不時的出現一些記憶體管理和垃圾回收的相關知識。這裡進行一個小小的總結。
Java是在JVM所虛擬出的記憶體環境中執行的。記憶體分為棧(stack)和堆(heap)兩部分。我們將分別考察這兩個區域。
棧
棧的基本概念參考 紙上談兵: 棧 (stack)。許多語言利用棧資料結構來記錄函式呼叫的次序和相關變數(參考 Linux從程式到程式)。
在Java中,JVM中的棧記錄了執行緒的方法呼叫。每個執行緒擁有一個棧。在某個執行緒的執行過程中,如果有新的方法呼叫,那麼該執行緒對應的棧就會增加一個儲存單元,即幀(frame)。在frame中,儲存有該方法呼叫的引數、區域性變數和返回地址。
呼叫棧
Java的引數和區域性變數只能是 基本型別的變數(比如int),或者物件的 引用(reference)。因此,在棧中,只儲存有基本型別的變數和物件引用。
引用所指向的物件儲存在堆中。(引用可能為Null值,即不指向任何物件)
引用與物件
當被呼叫方法執行結束時,該方法對應的幀將被刪除,引數和區域性變數所佔據的空間也隨之釋放。執行緒回到原方法,繼續執行。當所有的棧都清空時,程式也隨之執行結束。
堆
如上所述,棧(stack)可以自己照顧自己。但堆必須要小心對待。堆是JVM中一塊可自由分配給物件的區域。當我們談論垃圾回收(garbage collection)時,我們主要回收 堆(heap)的空間。
Java的普通物件存活在堆中。與棧不同,堆的空間不會隨著方法呼叫結束而清空。因此,在某個方法中建立的物件,可以在方法呼叫結束之後,繼續存在於堆中。這帶來的一個問題是,如果我們不斷的建立新的物件,記憶體空間將最終消耗殆盡。
垃圾回收
垃圾回收(garbage collection,簡稱GC)可以自動清空堆中不再使用的物件。垃圾回收機制最早出現於1959年,被用於解決Lisp語言中的問題。垃圾回收是Java的一大特徵。並不是所有的語言都有垃圾回收功能。比如在C/C++中,並沒有垃圾回收的機制。程式設計師需要手動釋放堆中的記憶體。
由於不需要手動釋放記憶體,程式設計師在程式設計中也可以減少犯錯的機會。利用垃圾回收,程式設計師可以避免一些指標和記憶體洩露相關的bug(這一類bug通常很隱蔽)。但另一方面,垃圾回收需要耗費更多的計算時間。垃圾回收實際上是將原本屬於程式設計師的責任轉移給計算機。使用垃圾回收的程式需要更長的執行時間。
在Java中,物件的是透過引用使用的(把物件相像成致命的毒物,引用就像是用於提取毒物的鑷子)。如果不再有引用指向物件,那麼我們就再也無從呼叫或者處理該物件。這樣的物件將 不可到達(unreachable)。垃圾回收用於 釋放不可到達物件所佔據的記憶體。這是垃圾回收的基本原則。
(不可到達物件是死物件,是垃圾回收所要回收的垃圾)
早期的垃圾回收採用
引用計數(reference counting)的機制。每個物件包含一個計數器。當有新的指向該物件的引用時,計數器加1。當引用移除時,計數器減1。當計數器為0時,認為該物件可以進行垃圾回收。
然而,一個可能的問題是,如果有兩個物件 迴圈引用(cyclic reference),比如兩個物件互相引用,而且此時沒有其它(指向A或者指向B)的引用,我們實際上根本無法透過引用到達這兩個物件。
因此,我們以棧和static資料為 根(root),從根出發,跟隨所有的引用,就可以找到所有的可到達物件。也就是說,一個可到達物件,一定被根引用,或者被其他可到達物件引用。
橙色,可到達;綠色,不可到達
JVM實施
JVM的垃圾回收是多種機制的混合。JVM會根據程式執行狀況,自行決定採用哪種垃圾回收。
我們先來了解 "mark and sweep"。這種機制下,每個物件將有標記資訊,用於表示該物件是否可到達。當垃圾回收啟動時,Java程式暫停執行。JVM從根出發,找到所有的可到達物件,並標記(mark)。隨後,JVM需要掃描整個堆,找到剩餘的物件,並清空這些物件所佔據的記憶體。
另一種是 "copy and sweep"。這種機制下,堆被分為兩個區域。物件總存活於兩個區域中的一個。當垃圾回收啟動時,Java程式暫停執行。JVM從根出發,找到可到達物件,將可到達物件複製到空白區域中並緊密排列,修改由於物件移動所造成的引用地址的變化。最後,直接清空物件原先存活的整個區域,使其成為新的空白區域。
可以看到,"copy and sweep"需要更加複雜的操作,但也讓物件可以緊密排列,避免"mark and sweep"中可能出現的空隙。在新建物件時,"copy and sweep"可以提供大塊的連續空間。因此,如果物件都比較"長壽",那麼適用於"mark and sweep"。如果物件的"新陳代謝"比較活躍,那麼適用於"copy and sweep"。
上面兩種機制是透過 分代回收(generational collection)混合在一起的。每個物件記錄有它的世代(generation)資訊。所謂的世代,是指該物件所經歷的垃圾回收的次數。世代越久遠的物件,在記憶體中存活的時間越久。
根據對Java程式的統計觀察,世代越久的物件,越不可能被垃圾回收(富人越富,窮人越窮)。因此,當我們在垃圾回收時,要更多關注那些年輕的物件。
現在,具體看一下JVM中的堆:
我們看到,堆分為三代。其中的 永久世代(permanent generation)中存活的是Class物件。這些物件不會被垃圾回收。我們在 RTTI中已經瞭解到,每個Class物件代表一個類,包含有類相關的資料與方法,並提供類定義的程式碼。每個物件在建立時,都要參照相應的Class物件。每個物件都包含有指向其對應Class物件的引用。
年輕世代(young generation)和
成熟世代(tenured generation)需要進行垃圾回收。年輕世代中的物件世代較近,而成熟世代中的物件世代較久。
世代
年輕世代進一步分為三個區域
eden(伊甸): 新生物件存活於該區域。新生物件指從上次GC後新建的物件。
新生物件生活於伊甸園
from, to: 這兩個區域大小相等,相當於copy and sweep中的兩個區域。
當新建物件無法放入eden區時,將出發minor collection。JVM採用copy and sweep的策略,將eden區與from區的可到達物件複製到to區。經過一次垃圾回收,eden區和from區清空,to區中則緊密的存放著存活物件。隨後,from區成為新的to區, to區成為新的from區。
如果進行minor collection的時候,發現to區放不下,則將部分物件放入成熟世代。另一方面,即使to區沒有滿,JVM依然會移動世代足夠久遠的物件到成熟世代。
如果成熟世代放滿物件,無法移入新的物件,那麼將觸發major collection。JVM採用mark and sweep的策略,對成熟世代進行垃圾回收。
總結
以上是對JVM記憶體管理的一個概述。實際上,JVM擁有眾多版本。不同版本實施的GC機制會有不小的差異。另一方面,Java本身並沒有規定JVM的GC實施方式。GC依然是JVM發展的一個熱點方向。我們可以預期JVM的GC機制在未來會發生許多變化。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31543790/viewspace-2674835/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java記憶體管理 -JVM 垃圾回收Java記憶體JVM
- PHP 垃圾回收與記憶體管理指引PHP記憶體
- .NET記憶體管理、垃圾回收記憶體
- java記憶體垃圾回收模型Java記憶體模型
- Node - 記憶體管理和垃圾回收記憶體
- JVM記憶體管理和垃圾回收JVM記憶體
- JavaScript 記憶體管理及垃圾回收JavaScript記憶體
- java基礎(一):談談java記憶體管理與垃圾回收機制Java記憶體
- [Java效能剖析]Sun JVM記憶體管理和垃圾回收JavaJVM記憶體
- Node記憶體限制與垃圾回收記憶體
- JVM的記憶體管理和垃圾回收JVM記憶體
- JVM垃圾回收器、記憶體分配與回收策略JVM記憶體
- 【JVM之記憶體與垃圾回收篇】JVM與Java體系結構JVM記憶體Java
- 垃圾回收與記憶體分配——總結篇記憶體
- 【JVM之記憶體與垃圾回收篇】堆JVM記憶體
- 淺談JVM記憶體分配與垃圾回收JVM記憶體
- V8 記憶體分配與垃圾回收記憶體
- javascript的垃圾回收機制和記憶體管理JavaScript記憶體
- Java記憶體模型,垃圾回收機制,常用記憶體命令及工具Java記憶體模型
- V8記憶體管理及垃圾回收機制記憶體
- JVM記憶體管理和垃圾回收機制介紹JVM記憶體
- Java面試題中高階進階(JVM篇Java垃圾回收)Java面試題JVM
- Node記憶體限制和垃圾回收記憶體
- 你必須瞭解的java記憶體管理機制(四)-垃圾回收Java記憶體
- JAVA的堆疊和記憶體、垃圾回收解說Java記憶體
- PHP的記憶體洩露問題與垃圾回收PHP記憶體洩露
- [譯] 通過垃圾回收機制理解 JavaScript 記憶體管理JavaScript記憶體
- golang 垃圾回收器如何標記記憶體?Golang記憶體
- JVM原理講解和調優,記憶體管理和垃圾回收,記憶體調優JVM記憶體
- JVM垃圾回收和記憶體分配策略JVM記憶體
- C# 垃圾回收釋放記憶體C#記憶體
- 探索JVM的垃圾回收(堆記憶體)JVM記憶體
- Python 記憶體管理方式和垃圾回收演算法Python記憶體演算法
- jvm:記憶體模型、記憶體分配及GC垃圾回收機制JVM記憶體模型GC
- 從JAVA記憶體到垃圾回收,帶你深入理解JVMJava記憶體JVM
- JVM 記憶體分代、垃圾回收漫談JVM記憶體
- iOS 進階—— iOS 記憶體管理 & BlockiOS記憶體BloC
- 【JVM之記憶體與垃圾回收篇】物件例項化記憶體佈局與訪問定位JVM記憶體物件