講解之前先插一句,對于Java的一些變量的存儲簡單了解一下:
1、本地變量(局部變量):在方法體, 構造體內(nèi)部定義的變量,在方法結束的時候就被摧毀(虛擬機棧的局部變量表)
2、靜態(tài)變量(類變量、全局變量;+ final 就是全局常量):在類里但在方法外, 加了 static 關鍵字. (靜態(tài)存儲區(qū)/全局區(qū)、方法區(qū))
3、實例變量:在類里但是不在方法里 在類被載入的時候被實例化(堆,和對象創(chuàng)新時一起分配內(nèi)存)
4、常量:方法區(qū)的常量池、常量存儲區(qū)
一、Java垃圾回收,它到底回收的是什么?
上篇文章介紹了Java內(nèi)存運行時數(shù)據(jù)區(qū)的各個部分,其中程序計數(shù)器、虛擬機棧、本地方法棧3個區(qū)域隨線程而生,隨線程而滅;棧中的棧幀隨著方法的進入和退出而有條不紊地執(zhí)行著出棧和入棧操作。每一個棧幀中分配多少內(nèi)存基本上是在類結構確定下來時就已知的,因此這幾個區(qū)域的內(nèi)存分配和回收都具備確定性,在這幾個區(qū)域內(nèi)就不需要過多考慮回收的問題,因為方法結束或者線程結束時,內(nèi)存自然就跟隨著回收了。而Java堆和方法區(qū)則不一樣,一個接口中的多個實現(xiàn)類需要的內(nèi)存可能不一樣,一個方法中的多個分支需要的內(nèi)存也可能不一樣,我們只有在程序處于運行期間時才能知道會創(chuàng)建哪些對象,這部分內(nèi)存的分配和回收都是動態(tài)的,所以Java的垃圾回收機制主要是在堆和方法區(qū)。
二、有哪些算法去標志這個對象是可以回收的呢?
1. 引用計數(shù)算法
給對象中添加一個引用計數(shù)器,每當有一個地方引用它時,計數(shù)器值就加1;當引用失效時,計數(shù)器值就減1;任何時刻計數(shù)器為0的對象就是不可能再被使用的。應用:python、object-c
缺點:它很難解決對象之間相互循環(huán)引用的問題
2. 可達性分析算法
這個算法的基本思路就是通過一系列的稱為“GC Roots”的對象作為起始點,從這些節(jié)點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。如圖對象object 5、object 6、object 7雖然互相有關聯(lián),但是它們到GC Roots是不可達的,所以它們將會被判定為是可回收的對象。應用:Java

什么對象可以作為“GC Roots”呢?
1、虛擬機棧(棧幀中的本地變量表)中引用的對象。
2、方法區(qū)中類靜態(tài)屬性引用的對象。
3、方法區(qū)中常量引用的對象。
4、本地方法棧中JNI(即一般說的Native方法)引用的對象。
三、有哪些算法去進行垃圾的回收呢?
上面是標志著這個對象是垃圾,接下去就是對這個垃圾進行回收。
1. 標記-清除算法
標記-清除算法分為兩個階段:標記階段和清除階段。標記階段的任務是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所占用的空間。
缺點:一個是效率問題,標記和清除兩個過程的效率都不高;另一個是空間問題,標記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導致以后在程序運行過程中需要分配較大對象時,無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作。

2. 復制算法
它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內(nèi)存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉。這樣使得每次都是對整個半?yún)^(qū)進行內(nèi)存回收,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復雜情況,只要移動堆頂指針,按順序分配內(nèi)存即可,實現(xiàn)簡單,運行高效。適用于存活對象少,回收對象多的情況下
缺點:內(nèi)存縮小為了原來的一半

3. 標記-整理算法
該算法標記階段和復制算法一樣,但是在完成標記之后,它不是直接清理可回收對象,而是將存活對象都向一端移動,然后清理掉端邊界以外的內(nèi)存。適用于存活對象多,回收對象少的情況下

4. 分代收集算法
Java的分代:老年代和新生代
新生代又分為:一塊較大的Eden空間和兩塊較小的Survivor空間,比例為8:1:1
為什么新生代要劃分為這樣的一個空間呢?
新生代采用的是復制算法,新的對象產(chǎn)生時就會先存放在Eden空間里,當遇到第一次GC時,會把Eden里面存活的對象復制到Survivor A,然后清除Eden和Survivor B的對象;第二次GC時,會把Eden和Survivor A里面存活的對象復制到Survivor B中;最后經(jīng)過幾次的GC之后就會把存活的對象轉移到老年代。就這樣需要三塊區(qū)域進行復制轉移。至于比例為什么是8:1:1,因為Eden區(qū)域比較頻繁的產(chǎn)生對象而Survivor存放的對象較少。
什么對象會被轉移到老年代呢?
1、經(jīng)過多次GC之后存活的對象
2、在Eden存不下的大的對象
3、存活下來了,但是Survivor存不下的大對象
在新生代中,每次垃圾收集時都發(fā)現(xiàn)有大批對象死去,只有少量存活,那就選用復制算法,只需要付出少量存活對象的復制成本就可以完成收集。而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記—整理”算法來進行回收。
四、小結
垃圾回收可以有效的防止內(nèi)存泄露,更好的使用內(nèi)存。