什么是“垃圾”管宵?
對(duì)我們已經(jīng)沒(méi)有使用價(jià)值的東西就稱為“垃圾”前痘。在 JVM 中無(wú)用的對(duì)象就稱為“垃圾”,因?yàn)樗加昧宋覀儗氋F的內(nèi)存資源坎拐。
哪里有“垃圾”烦磁?
前面我們已經(jīng)了解過(guò)了 JVM 的內(nèi)存,現(xiàn)在復(fù)習(xí)一下哼勇。我們知道程序計(jì)數(shù)器都伪、虛擬機(jī)棧、本地方法棧都是線程私有的积担,這幾個(gè)區(qū)域隨著線程消亡而消亡陨晶,可以達(dá)到自動(dòng)清理的效果。Java 堆帝璧、方法區(qū)則是共享區(qū)域先誉,各種對(duì)象的實(shí)例保存在 Java 堆中。所以這兩個(gè)區(qū)域會(huì)出現(xiàn)“垃圾”
誰(shuí)是“垃圾”的烁?
怎么確定誰(shuí)是垃圾褐耳,這里提供了兩種算法,分別是:引用計(jì)數(shù)算法撮躁、可達(dá)性分析算法
引用計(jì)數(shù)算法
引用計(jì)數(shù)算法的思路是這樣的:
每當(dāng)有一個(gè)地方引用它時(shí)漱病,計(jì)數(shù)器的值就加 1;當(dāng)引用失效的時(shí)候把曼,計(jì)數(shù)器就減 1杨帽。任何時(shí)刻計(jì)數(shù)器為 0 的對(duì)象就是不可能被再次使用的。
這種方法效率高嗤军,但是會(huì)出現(xiàn)無(wú)法回收的情況注盈,請(qǐng)看下面的代碼:
public class Main {
public static void main(String[] args) {
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
object1.object = object2;
object2.object = object1;
object1 = null;
object2 = null;
}
}
class MyObject{
public Object object = null;
}
這兩個(gè)對(duì)象已經(jīng)不可能再被訪問(wèn)了,但是因?yàn)樗麄冞€互相引用著對(duì)方叙赚,導(dǎo)致引用計(jì)數(shù)都不為 0老客,垃圾回收器無(wú)法回收他們僚饭。
可達(dá)性分析算法
這個(gè)算法的基本思路是:通過(guò)一系列的稱為 “GC Roots” 的對(duì)象作為起點(diǎn),從這些節(jié)點(diǎn)開(kāi)向下搜搜胧砰,搜索所走過(guò)的路徑稱為引用鏈鳍鸵,當(dāng)一個(gè)對(duì)象到 GC Roots 沒(méi)有任何引用鏈相連的時(shí)候就說(shuō)明對(duì)象是不可用的。
垃圾回收算法
標(biāo)記-清除(Mark-Sweep)算法
這是最基礎(chǔ)的垃圾回收算法尉间,思想簡(jiǎn)單偿乖,這個(gè)算法分為兩個(gè)階段,一個(gè)是“標(biāo)記”哲嘲,一個(gè)是“清除”贪薪。
算法大致思路:
標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成之后統(tǒng)一回收所有被標(biāo)記的對(duì)象
缺點(diǎn):效率低眠副,標(biāo)記和清除的效率都不高画切;標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片。
復(fù)制(Copying)算法
這個(gè)算法實(shí)現(xiàn)簡(jiǎn)單囱怕、高效霍弹、不容易產(chǎn)生碎片,但是將可用內(nèi)存縮小為原來(lái)內(nèi)存的一半娃弓,來(lái)看看這個(gè)算法是什么思路:
將可用內(nèi)存按容量分為大小相等的兩塊庞萍,每次使用其中的一塊,當(dāng)這塊內(nèi)存用完了忘闻,就將還存活著的對(duì)象復(fù)制到另外一塊內(nèi)存上面,然后把這塊已經(jīng)使用的內(nèi)存一次性清理掉恋博。簡(jiǎn)單粗暴齐佳。
標(biāo)記-整理(Mark-Compact)算法
算法標(biāo)記階段和Mark-Sweep一樣,但是在完成標(biāo)記之后债沮,它不是直接清理可回收對(duì)象炼吴,而是將存活對(duì)象都向一端移動(dòng),然后清理掉端邊界以外的內(nèi)存
分代收集算法
分代收集算法是目前大部分JVM的垃圾收集器采用的算法疫衩。根據(jù)對(duì)象存活的生命周期將內(nèi)存劃分為若干個(gè)不同的區(qū)域硅蹦。一般情況下將堆區(qū)劃分為老年代(Tenured Generation)和新生代(Young Generation),老年代的特點(diǎn)是每次垃圾收集時(shí)只有少量對(duì)象需要被回收闷煤,而新生代的特點(diǎn)是每次垃圾回收時(shí)都有大量的對(duì)象需要被回收童芹,那么就可以根據(jù)不同代的特點(diǎn)采取最適合的收集算法。
內(nèi)存分配與回收策略
我們知道鲤拿,對(duì)象的分配就是在 Java 堆上分配假褪,對(duì)象主要分配在新生代 Eden 區(qū)上,也可能分配在別的區(qū)域上(可設(shè)置內(nèi)存相關(guān)參數(shù))近顷。
對(duì)象優(yōu)先分配在 Eden
大多數(shù)情況下生音,對(duì)象在新生代 Eden 區(qū)域中分配宁否。如果 Eden 區(qū)域沒(méi)有足夠的空間分配的時(shí)候,就會(huì)觸發(fā) Minor GC
大對(duì)象直接進(jìn)入老年代
什么樣的對(duì)象稱為大對(duì)象缀遍?所謂的大對(duì)象是指需要大量連續(xù)內(nèi)存空間的 Java 對(duì)象慕匠,典型的大對(duì)象就是那種很長(zhǎng)的字符串以及數(shù)組。
長(zhǎng)期存活的對(duì)象將進(jìn)入老年代
虛擬機(jī)需要解決哪些對(duì)象放在新生代域醇,哪些對(duì)象應(yīng)該放在老年代的問(wèn)題台谊。為了解決這個(gè)問(wèn)題,虛擬機(jī)給對(duì)象定義了一個(gè)對(duì)象年齡的計(jì)數(shù)器歹苦。如果對(duì)象在 Eden 出生并且第一次 Minor GC 后仍然存活青伤,并且能被 Survivor 容納的話,將被移動(dòng)到 Survivor 空間中殴瘦,并且將對(duì)象的年齡設(shè)置為 1狠角。如果這個(gè)對(duì)象在 Survivor 空間中能夠熬過(guò) Minor GC,年齡再增加 1 歲蚪腋,當(dāng)年齡增加到一定程度時(shí)(默認(rèn)為 15 歲)丰歌,就會(huì)晉升到老年代。