標籤:
首先需要澄清的是,垃圾收集(GC)的曆史遠比Java要久遠,當我們意識到手動管理記憶體所帶來的麻煩時,懶惰的天性推動先驅們尋找更為簡單、易用、關鍵是傻瓜式的記憶體管理技術。GC技術起源於1960年誕生於MIT的Lisp語言,由此可見越聰明的人越懶惰。
最近有一種想法:程式開發,程式設計從本質上來講是個哲學問題,雖然我們要解決的問題是現實在程式世界中的投射,那麼問題本身就應該在現實世界中有相應的解決,而且我們也一直在類比現實世界,由過程式,物件導向以及函數式編程以及我們下面所要看到的GC的設計思想可見。 因為不想根據運行環境的不同修改代碼,我們希望有一個緩衝層能屏蔽底層硬體環境的不同,於是我們引入了JVM 因為有人工管理記憶體存在的不確定,JVM的設計者們引入了記憶體自動分配與垃圾收集 因為想要最大限度的利用記憶體,JVM記憶體被劃分為線程記憶體(棧)與共用記憶體堆 為了能夠更有效分配及回收記憶體,堆被劃分成了新生代與老年代,這樣才能根據對象的不同採用更加具有效率的GC方案。 也是因為不同GC方案的需求,新生代又被劃分為Eden區和survivor區 |---------- 新生代------------|------老年代------| |Eden|survivor|survivor| old | .... 1. 程式計數器、虛擬機器棧、本地方法棧三個地區隨這線程生死,其記憶體配置都具有確認性,因此不必過多考慮,我們所說的記憶體配置與回收通常是指的堆 2. 我們怎麼判斷一個對象”死了“,也就是可以回收的 2.1 引用計數器方法(未使用) 最直觀的方法,對象如果被引用,則引用計數+1,如果去引用,則引用計數-1,如果為0,則對象沒有引用,可以被回收。但該演算法無法解決循環參考的問題。JAVA虛擬機器也沒有採用該演算法。 2.2 根搜尋演算法 GC Root Tracing(Java、C#、Lisp) 通過一系列的名稱”GC Root“的對象作為起始點,從該節點向下搜尋,走過的路徑稱為引用鏈,所有不在任意一條引用鏈上的對象(不可達)就可以判斷為不可用。 該演算法需要確定Root點,在JAVA中包括如下幾種:虛擬機器棧中本地變數表所引用的對象、方法區中類靜態屬性引用的對象、方法區中常量引用的對象、本地方法棧JNI中引用的對象 2.3 finalize 在finalize塊中我們可以設定對象被重新引用,從而躲過GC,但不推薦在Finalize快中進行任何操作。因在finalize中的代碼塊會被放置在一個隊列中執行,且不保證執行結果。 3. 引用 Java的引用可以分為強引用(Strong Reference)、軟引用(SoftReference)、弱引用(WeakReference)、虛引用(PhantomReference)這四種,並且引用是由強到弱。 強引用是代碼中普遍存在的,類似於Object obj = new Obeject()類的引用,其永遠不會被回收 軟引用用來描述一些還有用,但並非必須的對象,在發生記憶體溢出前,會對軟引用的對象進行二次回收,仍無法分配記憶體時才會拋出異常。 弱引用用來描述非必須對象,被弱引用關聯的對象智能生存到下一次GC之前 虛引用是最弱的參考關聯性,其不會對對象產生影響,其設定的唯一目的就是在對象被回收時收到一個系統通知。 4. 方法區的回收 方法區也是有垃圾收集的,只是效果通常不好。方法區(永久代)的垃圾收集主要包括兩部分內容:廢棄常量和無用的類。在大量使用反射、動態代理、GClib等bytecode的情境都需要虛擬機器的卸載功能,以保證方法區不會溢出。
深入理解Java虛擬機器 - 垃圾收集概述