概述
- 哪些內存需要回收?
- 什麼時候回收?
- 如何回收?
程序計數器、虛擬機棧、本地方法棧3個區域隨線程而生,隨線程而滅;棧中的棧幀隨着方法的進入和退出而有條不紊地執行着出棧和入棧操作。而java堆和方法區則不一樣,只有在程序運行期間才能知道會創建哪些對象,這部分內存的分配和回收都是動態的,垃圾收集器所關注的是這部分內存。
**內存泄漏:**分配的內存得不到及時回收。
Java程序的內存分配和回收都是由JRE在後臺自動進行
的。(垃圾回收GC)JRE會提供一個後臺線程來進行檢測和控制。
【對象是否存活算法】
引用計數算法
主流的java虛擬機沒有選用引用計數算法來管理內存,主要的原因是它很難解決對象之間相互循環引用的問題。
可達性分析算法
基本思路是通過一系列的稱爲“GC Roots”的對象作爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的。
java語言中,可作爲GC Roots的對象包括下面幾種:
- 虛擬機棧中引用的對象。
- 方法區中類靜態屬性引用的對象。
- 方法區中常量引用的對象。
- 本地方法棧中JNI引用的對象。
引用
- 強引用:只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。
- 軟引用:SoftReference,系統將要發生內存溢出異常之前,將會把這些對象列進回收範圍之中進行第二次回收。如果這次回收還沒有足夠的內存,纔會拋出內存溢出異常。
- 弱引用:WeakReference,當垃圾收集器工作時,不管當前內存是否足夠,總會回收該對象所佔用的內存。
- 虛引用:PhantomReference,完全類似於沒有引用。主要用於跟蹤對象被垃圾回收的狀態,不能單獨使用,必須和引用隊列聯合使用。
【垃圾收集算法】
標記-清除算法
該算法分爲“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成後統一回收所有被標記的對象。
缺點:
- 效率低
- 空間問題,標記清除之後會產生大量不連續的內存碎片,空間碎片太多可能會導致以後在程序運行過程中需要分配較大對象時,無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。
複製算法
將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,然後再把已使用過的內存空間一次清理掉。
缺點:複製收集算法在對象存活率較高時就要進行較多的複製操作,效率將會變低。
標記-整理算法
標記過程仍然與“標記-清除”算法一樣,但後續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的內存。
分代收集算法
一般將java堆分爲新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集算法。新生代中,每次垃圾收集時都發現有大批對象死去,只有少量存活,那就選用複製算法。老生代中,對象存活率高、沒有額外空間對它進行分配擔保,就必須使用**“標記-整理”算法**來進行回收。
【垃圾收集器】
如果說收集算法是內存回收的方法論,那麼垃圾收集器就是內存回收的具體實現。
【內存分配】
大多數情況下,對象在新生代Eden區中分配。當Eden區沒有足夠空間進行分配時,虛擬機將發起一次Minor GC。
參考
- 深入理解Java虛擬機 第2版