GC可謂是java相較於C++語言,最大的不一樣點之一。java
上一篇講了內存的分佈。算法
其中程序計數器棧,虛擬機棧,本地方法棧 3個區域隨着線程而生,隨着線程而死。這些棧的內存,能夠理解爲在編譯期已經肯定。數組
方法結束,或者線程結束時,內存就天然被回收了。this
一個interface的多個實現類,須要的內存可能不同,一個方法的多個分支須要的內存也不同,咱們只有在程序運行的時候,才知道會建立那些對象,須要多少內存。線程
這部分分配和回收都是動態的,GC所關注的就是這部份內存。指針
java堆裏面幾乎存放着全部的對象實例。對象實例若是已經再也不被使用,那這段內存就應該被回收,這個時候就是GC上場的時候。對象
虛擬機棧,程序計數器,本地方法棧,這些都會隨着線程的消亡而消亡。因此這些不須要考慮內存的回收。ublog
GC的回收特指Java堆 & 方法區的內存。內存
給對象一個引用計數,當計數爲0的時候,能夠理解爲,該對象再也不被使用,能夠釋放內存。開發
可是這個方法很難解決一個問題:對象間的相互引用問題。
舉個例子:
對象objA和objB都有字段instance。
objA.instance = objB;
objB.instance = objA;
它們沒有其餘引用。
可是它們的引用計數不可能爲0.因而沒法通知GC回收它們。
主流的商用程序語言的主流實現中,都稱爲可達性分析。
這個算法的基本思想經過GC Roots的對象做爲起始點。當一個對象,到起始點,沒有鏈接的時候,能夠理解爲,這個節點的內存能夠釋放。
從圖中能夠看到,object5,object6,object7 這些對象會被GC回收。
不管經過何種算法來實現GC,都和引用有關。
java1.2以後,引用分爲 強引用,軟應用,弱引用,已級虛引用。
強引用是java廣泛存在的一種狀態,相似new 一個對象。強引用不會被GC回收。
軟引用,軟引用是描述還有用,但未必要存在的對象。它的生存時間是,當內存不足時,也就是快要OOM的時候,GC會回收它。
弱引用,就是非必要的的對象。被弱引用指向的對象,只能生存到下一次GC以前。
虛引用,虛引用的目的是爲了當GC發生時,能夠收到一個系統通知。不會對引用對象產生任何影響。
2.4 對象如何判斷要回收:
GC會對不可達的對象,作第一次標記。
當該對象執行finalize方法後,或者沒有覆蓋finalize方法,這回被第二次標記,這個時候,GC會被這段內存清理。
當對象執行finalize方法時,JVM會把它放在一個F-queue裏面,這是這個對象實例拯救本身的最後機會。
它只須要在finalize裏面,把this賦給某個變量。
任何一個對象finalize只會被執行一次。
3.垃圾收集的算法
顧名思義,分2端過程,先標記,再清除。先標記須要回收的內存,標記完成後,統一回收。
這個是最基礎的算法,其餘算法都是以此爲基礎。
2個缺點:效率問題,標記和清除效率都不高。內存碎片,標記清除以後會產生大量的空間碎片。若是申請大的內存
可能會申請不到,而不得不提早觸發下一次GC
就是將內存分爲大小相等的2塊,每次只使用一塊,當一塊用完的時候,就將還存活的部分,複製到另外一塊空間,而後
把已經使用的那塊作一次性清理,這種清理速度很是快。
這樣就不用考慮內存碎片的狀況。只是這種算法代價就是一半的內存空間。
標記過程同前面同樣,標記結束後,把內存塊移到一塊兒。(這裏的移動是指針移動,邏輯內存移動,物理內存應該沒有變化。)
讓全部存活的對象移向一端,而後直接清理掉端邊界之外的內存,清理速度很快,可是內存移動是耗時?。
從實現上說,清理應該是和硬盤刪除文件同樣,不是真正的刪除,而是把對應的內存標記爲 未使用。而且把文件名刪除。
根據對象存活週期的不一樣,分爲新生代,和老年代。
在新生代,每次回收都有不少對象被回收,能夠選用複製算法,只須要少許存活的對象複製成本就能夠。複製成本地,而且發生機率高,清理速度快。
而老年代,存活的時間比較久,發生機率地,對空間要求高,對時間要求低。必須使用標記清楚或者標記整理方法。
如今商業虛擬機的配置,因爲98%的對象可能會很快被釋放,因此複製算法並不是是1:1最合理。
HotSpot(Sun公司開發的虛擬機)分配就是一塊較大的Eden空間& 2塊Survivor空間。
比例位Eden:Survivor = 8:1.複製算法發生時,Eden+1Survivor 剩餘的內存 copy到1Survivor上。
在選擇處理GC Roots方法時,GC Root所在的上下文可能有數百兆,因此在判斷GC Roots的時候,應該確保一致性,JVM
應該中止全部java執行線程。因此GC是佔用CPU做爲代價!
java所解決的內存的管理歸根到底就是2個問題,內存的分配 & 已分配內存的回收。關於回收,咱們已經很詳細的分析GC的算法。
大多數狀況下,對象在新生代的Eden區中分配內存,當Eden區內存不足時,java虛擬機將發生GC,
釋放不須要的對象。
大對象,是指須要大量連續空間的對象,好比很長的字串或者數組。
比大對象更糟糕的事情就是,遇到一羣很快無用的 大對象。
大對象佔據內存空間,尤爲這些對象使用頻率不高,這樣就浪費了不少空間。容易照成GC的頻率過多。
對象開始放在新生代eden區,若是度過一次GC,進入Survivor空間,對象年齡設置爲1.
之後,每一次GC,年齡就加1。
當年齡到達必定的閥值之後(默認是15),就回進入老年代。
爲了充分利用新生代的內存空間,複製算法並無1:1的來分配。因此有機率當Eden還保留大量的對象時,1個Survivor沒法複製所有的對象
這個時候就須要部份內存放入老年代。But 若是老年代內存不足呢?那隻能繼續GC,以確保有足夠的空間。