Java垃圾回收
1. 垃圾回收
Java垃圾回收(GC)分為兩個階段:
判定,判定哪些對象可以被回收,使用可達性分析算法;
回收,回收那些無用的垃圾對象,常用的算法有:標記—清除算法、復制算法、標記—整理算法;
2. 可達性分析
當前的主流虛擬機都是使用可達性分析算法來判定一個對象是否還存活。
Java虛擬機中標記一系列稱為GC Root的對象,可達性分析算法的基本思想即判斷從這些GC Root對象到某個對象是否有引用路徑,如果有則說明該對象仍然有用,否則即判定該對象是垃圾對象,可以被回收。
可以作為GC Root對象的有:
?。?)虛擬機棧中引用的對象(方法中的局部變量)
?。?)方法區中類的靜態屬性引用的對象
?。?)方法區中的常量引用的對象
?。?)本地方法棧中引用的對象(Native方法中的局部變量)
3. 標記算法
標記對象死亡(一定會回收該對象)的具體過程:
(1)判定一個對象是否可達(使用可達性分析算法),如果不可達,將被標記為可回收;
?。?)然后進行第一次篩選,判斷該對象是否需要執行finalize方法,如果該對象沒有覆蓋finalize方法或者已經執行過finalize方法,則不需要執行finalize方法,否則需要執行finalize方法;
?。?)如果不需要執行finalize方法,則直接判定對象死亡,否則將該對象加入一個F-Queue隊列中,等待執行finalize方法;
?。?)然后進行第二次篩選,執行F-Queue隊列中對象的finalize方法,如果finalize方法中對象被再次引用,則對象起死回生,將不被回收,否則,對象就將要被回收了;
4. 垃圾回收算法
虛擬機決定回收一個已經被標記為死亡的對象,就需要使用垃圾回收算法。常用的垃圾回收算法有:標記—清除算法、復制算法、標記—整理算法,其中,復制算法和標記—整理算法是對標記—清除算法的改進。
(1)標記—清除算法
標記—清除算法是最基礎最簡單的算法,首先采用上面介紹的標記算法標記出死亡的對象,然后再統一回收所有被標記的對象的內存空間。
其中藍色表示存活著的對象,灰色表示尚未分配的內存,黃色表示待回收的內存。
這個算法有較大的缺點,標記清除之后內存中會有大量的內存碎片。
(2)復制算法
采用復制算法,將可用內存分為大小相同的兩部分A和B,A用來給對象分配內存,B保留不用。當A沒有足夠的空間分配時,則將所有存活著的對象復制到B,然后清理A中的內存空間。
復制算法的優點是清理內存的效率很高,也不會產生大量的內存碎片,但是一半的內存得不到利用,內存浪費實在太嚴重。
(3)標記—整理算法
與標記—清除算法不同的是,標記出死亡對象后,不是直接就回收對象,而是將存活的對象都移動到一起,緊湊內存,然后清理剩余的內存空間。
(4)分代回收算法
實際上,虛擬機中會將堆內存劃分成新生代和老年代,對于新生代采用改進的復制算法進行內存回收,對于老年代采用標記—整理算法進行內存回收。
在新生代中,將內存劃分成三部分,一個Eden部分,兩個Survivor(A和B)部分,HotSpot虛擬機中默認Eden和Survivor的大小比例是8:1。每次使用Eden部分和其中一個Survivor(A)部分,需要對這兩個部分進行內存回收時,采用復制算法,將尚且存活的對象復制到另一個Survivor(B)中,然后清理之前的Eden部分和Survivor(A)部分。
統計表明新生代內存中的對象存活時間通常較短,所以在進行內存回收時,存活的對象通常可以被容納在另外一個Survivor(B)中。如果出現另一個Survivor(B)無法容納全部存活的對象,先盡力復制到該Survivor(B)中,然后將剩余無法容納的對象復制到老年代中。
5. 內存分配策略
對象優先在新生代的Eden區中分配內存,當Eden區的內存不夠時將會發起一次GC;
大對象直接在老年代分配內存,例如:占用空間較多的數組通常就直接在老年代分配;
Eden區中的對象經過GC進入Survivor后,便有了年紀,初始值為1,以后每經過一次GC,年紀都會加1,直到某個設定的值時,這個對象就會被放進老年代中;
參考資料 《深入理解Java虛擬機》
總結
- 上一篇: 浅谈前端工程化、模块化、组件化
- 下一篇: 分布式深度学习计算框架依赖环境——NCC