JVM詳解 --- 垃圾回收機(jī)制

寫在前

在java中,程序員是不需要顯示的去釋放一個(gè)對(duì)象的內(nèi)存的忧陪,而是由虛擬機(jī)自行執(zhí)行。在JVM中近范,有一個(gè)垃圾回收線程嘶摊,它是低優(yōu)先級(jí)的,在正常情況下是不會(huì)執(zhí)行的评矩,只有在虛擬機(jī)空閑或者當(dāng)前堆內(nèi)存不足時(shí)更卒,才會(huì)觸發(fā)執(zhí)行,掃面那些沒有被任何引用的對(duì)象稚照,并將它們添加到要回收的集合中蹂空,進(jìn)行回收。

垃圾收集主要是針對(duì)堆和方法區(qū)進(jìn)行果录。程序計(jì)數(shù)器上枕、虛擬機(jī)棧和本地方法棧這三個(gè)區(qū)域?qū)儆诰€程私有的,只存在于線程的生命周期內(nèi)弱恒,線程結(jié)束之后就會(huì)消失辨萍,因此不需要對(duì)這三個(gè)區(qū)域進(jìn)行垃圾回收。

垃圾回收機(jī)制思維導(dǎo)圖

實(shí)際上返弹,Java技術(shù)體系中所提倡的 自動(dòng)內(nèi)存管理 最終可以歸結(jié)為自動(dòng)化地解決了兩個(gè)問題:給對(duì)象分配內(nèi)存 以及回收分配給對(duì)象的內(nèi)存锈玉,而且這兩個(gè)問題針對(duì)的內(nèi)存區(qū)域就是Java內(nèi)存模型中的堆區(qū)

我們知道垃圾回收機(jī)制是Java語言一個(gè)顯著的特點(diǎn)义起,其可以有效的防止內(nèi)存泄露拉背、保證內(nèi)存的有效使用(Ps: 內(nèi)存泄露是指該內(nèi)存空間使用完畢之后未回收,在不涉及復(fù)雜數(shù)據(jù)結(jié)構(gòu)的一般情況下默终,Java 的內(nèi)存泄露表現(xiàn)為一個(gè)內(nèi)存對(duì)象的生命周期超出了程序需要它的時(shí)間長(zhǎng)度)椅棺,從而使得Java程序員在編寫程序的時(shí)候不再需要考慮內(nèi)存管理問題。Java 垃圾回收機(jī)制要考慮的問題很復(fù)雜齐蔽,這里主要闡述其三個(gè)核心問題两疚,包括:哪些內(nèi)存需要回收?什么時(shí)候回收含滴?如何回收诱渤?

在探討Java垃圾回收機(jī)制之前,我們首先應(yīng)該記住一個(gè)單詞:Stop-the-World谈况。Stop-the-world意味著 JVM由于要執(zhí)行GC而停止了應(yīng)用程序的執(zhí)行勺美,并且這種情形會(huì)在任何一種GC算法中發(fā)生鞋吉。當(dāng)Stop-the-world發(fā)生時(shí),除了GC所需的線程以外励烦,所有線程都處于等待狀態(tài)直到GC任務(wù)完成谓着。事實(shí)上,GC優(yōu)化很多時(shí)候就是指減少Stop-the-world發(fā)生的時(shí)間坛掠,從而使系統(tǒng)具有 高吞吐 赊锚、低停頓 的特點(diǎn)。

1屉栓、內(nèi)存如何分配和回收的

(1)Java內(nèi)存分配模型

Java 的自動(dòng)內(nèi)存管理主要是針對(duì)對(duì)象內(nèi)存的回收和對(duì)象內(nèi)存的分配舷蒲。同時(shí),Java 自動(dòng)內(nèi)存管理最核心的功能是 堆 內(nèi)存中對(duì)象的分配與回收友多。

Java 堆是垃圾收集器管理的主要區(qū)域牲平,因此也被稱作GC 堆(Garbage Collected Heap)。從垃圾回收的角度域滥,由于現(xiàn)在收集器基本都采用分代垃圾收集算法纵柿,所以 Java 堆還可以細(xì)分為:新生代和老年代:再細(xì)致一點(diǎn)有:Eden 空間、From Survivor启绰、To Survivor 空間等昂儒。進(jìn)一步劃分的目的是更好地回收內(nèi)存,或者更快地分配內(nèi)存委可。

堆空間的基本結(jié)構(gòu):

(2)Java堆內(nèi)存分配策略

堆內(nèi)存常見的分配策略
  • 在堆中渊跋,如果待分配的對(duì)象所需內(nèi)存大于eden區(qū)大小,那么將直接送入老年代着倾。
  • 如果eden區(qū)可以放下拾酝,會(huì)經(jīng)歷一下的分配過程:
    • 對(duì)象都會(huì)首先在 Eden 區(qū)域分配
    • 第一次垃圾回收,將Eden存活對(duì)象放入From區(qū)卡者,清空Eden區(qū)
    • 第二次垃圾回收蒿囤,將Eden和From存活對(duì)象放到To區(qū),清空Eden和From區(qū)虎眨,ps:如果上次From區(qū)對(duì)象還存活蟋软,將對(duì)象的年齡還會(huì)加 1(初始為0)當(dāng)它的年齡增加到一定程度(默認(rèn)為 15 歲)镶摘,就會(huì)被晉升到老年代中嗽桩。
    • 經(jīng)過這次GC后,交換From和To區(qū)凄敢,保證To區(qū)始終是空的碌冶。

(3)新生代內(nèi)存中,為什么要有Survivor區(qū)域

  • 如果沒有Survivor涝缝,Eden區(qū)每進(jìn)行一次Minor GC(發(fā)生在新生代的垃圾回收)扑庞,存活的對(duì)象就會(huì)被送到老年代譬重。老年代很快被填滿,觸發(fā)Major GC(因?yàn)镸ajor GC一般伴隨著Minor GC罐氨,也可以看做觸發(fā)了Full GC)臀规。老年代的內(nèi)存空間遠(yuǎn)大于新生代,進(jìn)行一次Full GC消耗的時(shí)間比Minor GC長(zhǎng)得多栅隐。你也許會(huì)問塔嬉,執(zhí)行時(shí)間長(zhǎng)有什么壞處?頻發(fā)的Full GC消耗的時(shí)間是非匙馇模可觀的谨究,這一點(diǎn)會(huì)影響大型程序的執(zhí)行和響應(yīng)速度,更不要說某些連接會(huì)因?yàn)槌瑫r(shí)發(fā)生連接錯(cuò)誤了泣棋。
  • 從上面可以看出來胶哲,Survivor區(qū)域帶來的最大的優(yōu)勢(shì)就是防止老年代被很快填滿,從而增大老年代垃圾回收時(shí)間上的浪費(fèi)

好潭辈,那我們來想想在沒有Survivor的情況下鸯屿,有沒有什么解決辦法,可以避免上述情況:

  • 增加老年代空間 把敢,更多存活對(duì)象才能填滿老年代碾盟。降低Full GC頻率 隨著老年代空間加大,一旦發(fā)生Full GC技竟,執(zhí)行所需要的時(shí)間更長(zhǎng)
  • 減少老年代空間 Full GC所需時(shí)間減少 老年代很快被存活對(duì)象填滿冰肴,F(xiàn)ull GC頻率增加。顯而易見榔组,沒有Survivor的話熙尉,上述兩種解決方案都不能從根本上解決問題。

(4)為什么要設(shè)置兩個(gè)Survivor區(qū)

這個(gè)問題也就是復(fù)制算法的原理搓扯,堆中新生代采用的就是復(fù)制算法检痰,下面來看一下它的魅力:

設(shè)置兩個(gè)Survivor區(qū)最大的好處就是解決了內(nèi)存碎片化,我們來分析一下:

1)首先使用單個(gè)Survivor區(qū)

  • 剛剛新建的對(duì)象在Eden中锨推,一旦Eden滿了铅歼,觸發(fā)一次Minor GC,Eden中的存活對(duì)象就會(huì)被移動(dòng)到Survivor區(qū)换可。這樣繼續(xù)循環(huán)下去椎椰,下一次Eden滿了的時(shí)候,問題來了沾鳄,此時(shí)進(jìn)行Minor GC慨飘,Eden和Survivor各有一些存活對(duì)象,如果此時(shí)把Eden區(qū)的存活對(duì)象硬放到Survivor區(qū),很明顯這兩部分對(duì)象所占有的內(nèi)存是不連續(xù)的瓤的,也就導(dǎo)致了內(nèi)存碎片化休弃。
  • 碎片化帶來的風(fēng)險(xiǎn)是極大的,嚴(yán)重影響JAVA程序的性能塔猾。堆空間被散布的對(duì)象占據(jù)不連續(xù)的內(nèi)存桥帆,最直接的結(jié)果就是慎皱,堆中沒有足夠大的連續(xù)內(nèi)存空間茫多,接下去如果程序需要給一個(gè)內(nèi)存需求很大的對(duì)象分配內(nèi)存,就會(huì)造成很可怕的結(jié)果夺欲。下面用圖簡(jiǎn)單說明一下只有一個(gè)Survivor會(huì)出現(xiàn)什么樣的情況
  • 第一次GC發(fā)生之前些阅,Survivor區(qū)為空斑唬, GC過后市埋, Eden清空, Survivor填上
  • 第二次GC的時(shí)候恕刘,Survivor區(qū)域中有部分被標(biāo)記清除缤谎,Eden又加入了一些新的元素, 那么繼續(xù)清空如下褐着,可以看到此輪GC過后坷澡,Survivor區(qū)因?yàn)榇溯喴睬宄藘蓚€(gè)紅點(diǎn),所以產(chǎn)生了兩個(gè)碎片含蓉,而Eden區(qū)新加入的四個(gè)綠點(diǎn)緊跟著之前的Survivor加入
  • 第三次GC的時(shí)候频敛,同樣的Eden區(qū)域和Survivor區(qū)域都產(chǎn)生了要回收的對(duì)象,看看這會(huì)出現(xiàn)了什么樣的情況

經(jīng)過上面的GC馅扣,可以看到最終再Survivor區(qū)出現(xiàn)了大量的碎片斟赚,那么解決這個(gè)問題的最好的方式就是使用兩個(gè)Survivor區(qū)。

2)使用兩個(gè)Survivor區(qū)

咱們?cè)賮砜纯雌裆ぃ脙蓚€(gè)Survivor會(huì)出現(xiàn)什么樣的情況躲惰。

  • 首先第一次GC端三,將Eden區(qū)紅色全部干掉,綠色(存活的對(duì)象)全部扔到from里面去。
  • 這里簡(jiǎn)單看一下第二次GC是怎么進(jìn)行操作的辐宾,首先Eden區(qū)和from區(qū)這個(gè)時(shí)候又出現(xiàn)了許多紅點(diǎn)(待清理的對(duì)象),這次JVM的操作就不是直接再Survivor區(qū)域上將對(duì)象干掉铲汪,這次他會(huì)收先將from區(qū)域里面的紅點(diǎn)全部干掉,然后剩余的綠點(diǎn)順位進(jìn)入to區(qū)域齿梁,eden區(qū)域同樣按這個(gè)原理清空, 放入to區(qū)域之后省核,再將from區(qū)域和to區(qū)域交換,始終保證to區(qū)域是空閑的狀態(tài)笔刹,這樣就可以非常完美的解決碎片化問題。

2.哪些內(nèi)存需要回收日月?

堆中幾乎放著所有的對(duì)象實(shí)例,對(duì)堆垃圾回收前的第一步就是要判斷那些對(duì)象已經(jīng)死亡(即不能再被任何途徑使用的對(duì)象)精拟。

(1)怎么判斷對(duì)象已死亡

判斷對(duì)象是否已經(jīng)死亡通常有兩個(gè)方法,引用計(jì)數(shù)法和可達(dá)性分析算法怪瓶。

1)引用計(jì)數(shù)法

給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它,計(jì)數(shù)器就加 1绎晃;當(dāng)引用失效,計(jì)數(shù)器就減 1落竹;任何時(shí)候計(jì)數(shù)器為 0 的對(duì)象就是不可能再被使用的。

這個(gè)方法實(shí)現(xiàn)簡(jiǎn)單积暖,效率高,但是目前主流的虛擬機(jī)中并沒有選擇這個(gè)算法來管理內(nèi)存遍愿,其最主要的原因是它很難解決對(duì)象之間循環(huán)引用的問題。

2)可達(dá)性分析算法

這個(gè)算法的基本思想就是通過一系列的稱為 “GC Roots” 的對(duì)象作為起點(diǎn)坞笙,從這些節(jié)點(diǎn)開始向下搜索籍茧,節(jié)點(diǎn)所走過的路徑稱為引用鏈院水,當(dāng)一個(gè)對(duì)象到 GC Roots 沒有任何引用鏈相連的話撬腾,則證明此對(duì)象是不可用的。

可達(dá)性算法

ps:a, b 對(duì)象可回收,就一定會(huì)被回收嗎?

并不是喧半,對(duì)象的 finalize 方法給了對(duì)象一次垂死掙扎的機(jī)會(huì),當(dāng)對(duì)象不可達(dá)(可回收)時(shí)挺据,當(dāng)發(fā)生GC時(shí),會(huì)先判斷對(duì)象是否執(zhí)行了 finalize 方法婉称,如果未執(zhí)行,則會(huì)先執(zhí)行 finalize 方法瘫筐,我們可以在此方法里將當(dāng)前對(duì)象與 GC Roots 關(guān)聯(lián)肛捍,這樣執(zhí)行 finalize 方法之后拙毫,GC 會(huì)再次判斷對(duì)象是否可達(dá),如果不可達(dá)缺前,則會(huì)被回收,如果可達(dá)逝段,則不回收!

注意: finalize 方法只會(huì)被執(zhí)行一次嘹黔,如果第一次執(zhí)行 finalize 方法此對(duì)象變成了可達(dá)確實(shí)不會(huì)回收,但如果對(duì)象再次被 GC浙值,則會(huì)忽略 finalize 方法,對(duì)象會(huì)被回收筐付!這一點(diǎn)切記!

ps:哪些對(duì)象可以作為 GC Root 呢?

便于記憶较解,稱他為兩棧兩方法(引用的對(duì)象)啡捶!

  • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象;
  • 本地方法棧中 JNI(即一般說的 Native 方法)引用的對(duì)象
  • 方法區(qū)中類靜態(tài)屬性引用的對(duì)象(如類中使用的static聲明的引用類型字段)
  • 方法區(qū)中常量引用的對(duì)象(如類中使用final聲明的引用類型字段)

(2)四種引用是怎么進(jìn)行垃圾回收的

JDK1.2 以后,Java 對(duì)引用的概念進(jìn)行了擴(kuò)充鲤桥,將引用分為強(qiáng)引用、軟引用贮喧、弱引用、虛引用四種(引用強(qiáng)度逐漸減弱)

1)強(qiáng)引用(StrongReference)

有強(qiáng)引用的對(duì)象谓形,垃圾回收器絕不會(huì)回收它,當(dāng)內(nèi)存空間不足童太,Java 虛擬機(jī)寧愿拋出 OutOfMemoryError 錯(cuò)誤,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來解決內(nèi)存不足問題扯再。

2)軟引用(SoftReference)

如果內(nèi)存空間足夠齿穗,垃圾回收器就不會(huì)回收它,如果內(nèi)存空間不足了脖卖,就會(huì)回收這些對(duì)象的內(nèi)存砸泛。常見應(yīng)用場(chǎng)景:緩存

3)弱引用(WeakReference)

在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中勾栗,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存漾抬。

4)虛引用(PhantomReference)

虛引用主要用來跟蹤對(duì)象被垃圾回收器回收的活動(dòng)。虛引用與軟引用和弱引用的一個(gè)區(qū)別在于:虛引用必須和引用隊(duì)列 (ReferenceQueue)聯(lián)合使用坤按。當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí)腹忽,如果發(fā)現(xiàn)它還有虛引用,就會(huì)在回收對(duì)象的內(nèi)存之前葫录,把這個(gè)虛引用加入到與之 關(guān)聯(lián)的引用隊(duì)列中面粮。你聲明虛引用的時(shí)候是要傳入一個(gè)queue的熬苍。當(dāng)你的虛引用所引用的對(duì)象已經(jīng)執(zhí)行完finalize函數(shù)的時(shí)候,就會(huì)把對(duì)象加到queue里面袁翁。你可以通過判斷queue里面是不是有對(duì)象來判斷你的對(duì)象是不是要被回收了【這是重點(diǎn)柴底,讓你知道你的對(duì)象什么時(shí)候會(huì)被回收。因?yàn)閷?duì)普通的對(duì)象梦裂,gc要回收它的似枕,你是知道它什么時(shí)候會(huì)被回收】冗恨。

特別注意,在程序設(shè)計(jì)中一般很少使用弱引用與虛引用疟位,使用軟引用的情況較多傻铣,這是因?yàn)?strong>軟引用可以加速 JVM 對(duì)垃圾內(nèi)存的回收速度缆瓣,可以維護(hù)系統(tǒng)的運(yùn)行安全族吻,防止內(nèi)存溢出(OutOfMemory)等問題的產(chǎn)生懊悯。

(3)JVM的方法區(qū)可以實(shí)現(xiàn)垃圾回收嗎?

方法區(qū)的垃圾回收主要有兩種七兜,分別是對(duì)廢棄常量的回收和對(duì)無用類的回收。

  • 當(dāng)一個(gè)常量對(duì)象不再任何地方被引用的時(shí)候迄薄,則被標(biāo)記為廢棄常量,這個(gè)常量可以被回收。假如在常量池中存在字符串 "abc"坝锰,如果當(dāng)前沒有任何 String 對(duì)象引用該字符串常量的話翔冀,就說明常量 "abc" 就是廢棄常量说订,如果這時(shí)發(fā)生內(nèi)存回收的話而且有必要的話,"abc" 就會(huì)被系統(tǒng)清理出常量池。

類需要同時(shí)滿足下面 3 個(gè)條件才能算是 “無用的類”

  • 該類所有的實(shí)例都已經(jīng)被回收漓概,也就是 Java 堆中不存在該類的任何實(shí)例仆潮。
  • 加載該類的 ClassLoader 已經(jīng)被回收武通。
  • 該類對(duì)應(yīng)的 java.lang.Class 對(duì)象沒有在任何地方被引用沛鸵,無法在任何地方通過反射訪問該類的方法趾徽。

虛擬機(jī)可以對(duì)滿足上述 3 個(gè)條件的無用類進(jìn)行回收,這里說的僅僅是“可以”谊囚,而并不是和對(duì)象一樣不使用了就會(huì)必然被回收。

3.什么時(shí)候回收全景?

(1)分代垃圾回收器工作流程

分代回收器有兩個(gè)分區(qū):老生代和新生代耀石,新生代默認(rèn)的空間占比總空間的 1/3,老生代的默認(rèn)占比是 2/3爸黄。

新生代使用的是復(fù)制算法滞伟,新生代里有 3 個(gè)分區(qū):Eden、To Survivor炕贵、From Survivor梆奈,它們的默認(rèn)占比是 8:1:1,它的執(zhí)行流程如下:

  • 把 Eden + From Survivor 存活的對(duì)象放入 To Survivor 區(qū)称开;
  • 清空 Eden 和 From Survivor 分區(qū)亩钟;
  • From Survivor 和 To Survivor 分區(qū)交換,F(xiàn)rom Survivor 變 To Survivor鳖轰,To Survivor 變 From Survivor清酥。

每次在 From Survivor 到 To Survivor 移動(dòng)時(shí)都存活的對(duì)象,年齡就 +1蕴侣,當(dāng)年齡到達(dá) 15(默認(rèn)配置是 15)時(shí)焰轻,升級(jí)為老生代。大對(duì)象也會(huì)直接進(jìn)入老生代昆雀。

老生代當(dāng)空間占用到達(dá)某個(gè)值之后就會(huì)觸發(fā)全局垃圾收回(full gc)鹦马,一般使用標(biāo)記整理的執(zhí)行算法胧谈。以上這些循環(huán)往復(fù)就構(gòu)成了整個(gè)分代垃圾回收的整體執(zhí)行流程。

(2)Minor GC和Major GC內(nèi)存回收策略

多數(shù)情況荸频,對(duì)象都在新生代 Eden 區(qū)分配。當(dāng) Eden 區(qū)分配沒有足夠的空間進(jìn)行分配時(shí)客冈,虛擬機(jī)將會(huì)發(fā)起一次 Minor GC旭从。如果本次 GC 后還是沒有足夠的空間,則將啟用分配擔(dān)保機(jī)制在老年代中分配內(nèi)存场仲。

ps:分配擔(dān)保機(jī)制和悦,在發(fā)生Minor GC之前,虛擬機(jī)會(huì)先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對(duì)象總空間渠缕,如果這個(gè)條件成立鸽素,那么Minor GC可以確保是安全的。當(dāng)大量對(duì)象在Minor GC后仍然存活亦鳞,就需要老年代進(jìn)行空間分配擔(dān)保馍忽,把Survivor無法容納的對(duì)象直接進(jìn)入老年代。如果老年代判斷到剩余空間不足(根據(jù)以往每一次回收晉升到老年代對(duì)象空間的平均值作為經(jīng)驗(yàn)值)燕差,則進(jìn)行一次Full GC遭笋。

這里我們提到 Minor GC,如果你仔細(xì)觀察過 GC 日常徒探,通常我們還能從日志中發(fā)現(xiàn) Major GC/Full GC瓦呼。

  • Minor GC:對(duì)新生代進(jìn)行回收,不會(huì)影響到年老代测暗。因?yàn)樾律?Java 對(duì)象大多死亡頻繁央串,所以 Minor GC 非常頻繁,一般在這里使用速度快碗啄、效率高的算法质和,使垃圾回收能盡快完成。
  • Full GC:也叫 Major GC挫掏,對(duì)整個(gè)堆進(jìn)行回收侦另,包括新生代和老年代。由于Full GC需要對(duì)整個(gè)堆進(jìn)行回收尉共,所以比Minor GC要慢褒傅,因此應(yīng)該盡可能減少Full GC的次數(shù),導(dǎo)致Full GC的原因包括:老年代被寫滿袄友、永久代(Perm)被寫滿和System.gc()被顯式調(diào)用等殿托。

ps:永久代(Perm Generation):主要存放元數(shù)據(jù),例如Class剧蚣、Method的元信息支竹,與垃圾回收要回收的Java對(duì)象關(guān)系不大旋廷。相對(duì)于新生代和年老代來說,該區(qū)域的劃分對(duì)垃圾回收影響比較小礼搁。

(3)可以主動(dòng)通知虛擬機(jī)進(jìn)行垃圾回收嗎

可以饶碘。程序員可以手動(dòng)執(zhí)行System.gc(),通知GC運(yùn)行馒吴,但是Java語言規(guī)范并不保證GC一定會(huì)執(zhí)行扎运。

4.如何回收(回收策略)?

(1)垃圾收集算法

1)標(biāo)記清除算法

該算法分為“標(biāo)記”和“清除”階段:首先比較出所有需要回收的對(duì)象饮戳,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象豪治。它是最基礎(chǔ)的收集算法,后續(xù)的算法都是對(duì)其不足進(jìn)行改進(jìn)得到扯罐。這種垃圾收集算法會(huì)帶來兩個(gè)明顯的問題:

  • 效率問題
  • 空間問題(標(biāo)記清除后會(huì)產(chǎn)生大量不連續(xù)的碎片)
標(biāo)記清除算法

2)復(fù)制算法(效率優(yōu)化负拟,不會(huì)產(chǎn)生內(nèi)存碎片)

為了解決效率問題,“復(fù)制”收集算法出現(xiàn)了歹河。它可以將內(nèi)存分為大小相同的兩塊掩浙,每次使用其中的一塊。當(dāng)這一塊的內(nèi)存使用完后启泣,就將還存活的對(duì)象復(fù)制到另一塊去涣脚,然后再把使用的空間一次清理掉。這樣就使每次的內(nèi)存回收都是對(duì)內(nèi)存區(qū)間的一半進(jìn)行回收寥茫。缺點(diǎn):降低了內(nèi)存利用率遣蚀。

復(fù)制算法

3)標(biāo)記整理算法(不會(huì)產(chǎn)生內(nèi)存碎片)

根據(jù)老年代的特點(diǎn)提出的一種標(biāo)記算法,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣纱耻,但后續(xù)步驟不是直接對(duì)可回收對(duì)象回收芭梯,而是讓所有存活的對(duì)象向一端移動(dòng),然后直接清理掉邊界以外的內(nèi)存弄喘。(存活對(duì)象較多玖喘,不會(huì)產(chǎn)生內(nèi)存碎片)

標(biāo)記整理算法

4)分代收集算法

虛擬機(jī)的垃圾收集都采用分代收集算法,這種算法沒有什么新的思想蘑志,只是根據(jù)對(duì)象存活周期的不同將內(nèi)存分為幾塊累奈。一般將 Java 堆分為新生代和老年代,這樣我們就可以根據(jù)各個(gè)年代的特點(diǎn)選擇合適的垃圾收集算法急但。

  • 在新生代使用復(fù)制算法
  • 在老年代使用標(biāo)記整理算法

(2)垃圾收集器

如果說收集算法是內(nèi)存回收的方法論澎媒,那么垃圾收集器就是內(nèi)存回收的具體實(shí)現(xiàn)。垃圾回收器的工作流程大體如下:

  • 標(biāo)記出哪些對(duì)象是存活的波桩,哪些是垃圾(可回收)戒努;
  • 進(jìn)行回收(清除/復(fù)制/整理),如果有移動(dòng)過對(duì)象(復(fù)制/整理)镐躲,還需要更新引用储玫。

下圖展示了7種作用于不同分代的收集器侍筛,其中用于回收新生代的收集器包括Serial、PraNew撒穷、Parallel Scavenge匣椰,回收老年代的收集器包括Serial Old、Parallel Old桥滨、CMS窝爪,還有用于回收整個(gè)Java堆的G1收集器。不同收集器之間的連線表示它們可以搭配使用齐媒。

垃圾回收器

注意:jdk1.8 默認(rèn)垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代),但是一般我們都不會(huì)使用纷跛,但是在jdk1.9 默認(rèn)垃圾收集器G1喻括。

  • Serial收集器(復(fù)制算法): 新生代單線程收集器,標(biāo)記和清理都是單線程贫奠,優(yōu)點(diǎn)是簡(jiǎn)單高效唬血;
Serial收集器
  • ParNew收集器 (復(fù)制算法): 新生代收并行集器,實(shí)際上是Serial收集器的多線程版本唤崭,在多核CPU環(huán)境下有著比Serial更好的表現(xiàn)拷恨;ps:也需要stop the world
ParNew收集器
  • Parallel Scavenge收集器 (復(fù)制算法): 新生代并行收集器,追求高吞吐量谢肾,高效利用 CPU腕侄。吞吐量 = 用戶線程時(shí)間/(用戶線程時(shí)間+GC線程時(shí)間),高吞吐量可以高效率的利用CPU時(shí)間芦疏,盡快完成程序的運(yùn)算任務(wù)冕杠,適合后臺(tái)應(yīng)用等對(duì)交互相應(yīng)要求不高的場(chǎng)景;
Parallel Scavenge 收集器
  • Serial Old收集器 (標(biāo)記-整理算法): 老年代單線程收集器酸茴,Serial收集器的老年代版本分预;

  • Parallel Old收集器 (標(biāo)記-整理算法): 老年代并行收集器,吞吐量?jī)?yōu)先薪捍,Parallel Scavenge收集器的老年代版本笼痹;

  • CMS(Concurrent Mark Sweep)收集器(標(biāo)記-清除算法): 老年代并行收集器,它非常符合在注重用戶體驗(yàn)的應(yīng)用上使用酪穿,以獲取最短回收停頓時(shí)間為目標(biāo)的收集器凳干,具有高并發(fā)、低停頓的特點(diǎn)昆稿,追求最短GC回收停頓時(shí)間纺座。

  • G1(Garbage First)收集器 (標(biāo)記-整理算法): Java堆并行收集器,G1收集器是JDK1.7提供的一個(gè)新收集器溉潭,G1收集器基于“標(biāo)記-整理”算法實(shí)現(xiàn)净响,也就是說不會(huì)產(chǎn)生內(nèi)存碎片少欺。此外,G1收集器不同于之前的收集器的一個(gè)重要特點(diǎn)是:G1回收的范圍是整個(gè)Java堆(包括新生代馋贤,老年代)赞别,而前六種收集器回收的范圍僅限于新生代或老年代。

因?yàn)镃MS和G1收集器相對(duì)比較特殊配乓,下面單獨(dú)介紹一下他們

(3)CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間【也就是指Stop The World的停頓時(shí)間】為目標(biāo)的收集器仿滔。它非常符合在注重用戶體驗(yàn)的應(yīng)用上使用。

CMS(Concurrent Mark Sweep)收集器是 HotSpot 虛擬機(jī)第一款真正意義上的并發(fā)收集器犹芹,它第一次實(shí)現(xiàn)了讓垃圾收集線程與用戶線程(基本上)同時(shí)工作崎页。

從名字中的Mark Sweep這兩個(gè)詞可以看出,CMS 收集器是一種 “標(biāo)記-清除”算法實(shí)現(xiàn)的腰埂,多數(shù)應(yīng)用于互聯(lián)網(wǎng)站或者B/S系統(tǒng)的服務(wù)器端上飒焦。其中“Concurrent”并發(fā)是指垃圾收集的線程和用戶執(zhí)行的線程是可以同時(shí)執(zhí)行的。它的運(yùn)作過程相比于前面幾種垃圾收集器來說更加復(fù)雜一些屿笼。整個(gè)過程分為四個(gè)步驟:

  • 初始標(biāo)記: 暫停所有的其他線程牺荠,只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象,速度很快驴一;
  • 并發(fā)標(biāo)記: 同時(shí)開啟 GC 和用戶線程(不會(huì)阻礙業(yè)務(wù)線程繼續(xù)執(zhí)行)休雌,就是從GC Roots開始找到它能引用的所有對(duì)象的過程。原因:不能保證當(dāng)前所有對(duì)象可達(dá)肝断,因?yàn)橛脩艟€程可能會(huì)不斷的更新引用域杈曲,所以GC 線程無法保證可達(dá)性分析的實(shí)時(shí)性。所以這個(gè)算法里會(huì)跟蹤記錄這些發(fā)生引用更新的地方孝情。
  • 重新標(biāo)記: 重新標(biāo)記階段就是為了修正并發(fā)標(biāo)記期間因?yàn)橛脩舫绦蚶^續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄鱼蝉,這個(gè)階段的停頓時(shí)間一般會(huì)比初始標(biāo)記階段的時(shí)間稍長(zhǎng),遠(yuǎn)遠(yuǎn)比并發(fā)標(biāo)記階段時(shí)間短箫荡。
  • 并發(fā)清除: 開啟用戶線程魁亦,同時(shí) GC 線程開始對(duì)未標(biāo)記的區(qū)域做清掃。

ps:初始標(biāo)記羔挡、重新標(biāo)記這兩個(gè)步驟仍然需要“Stop The World”洁奈。

CMS 垃圾收集器

從它的名字就可以看出它是一款優(yōu)秀的垃圾收集器,主要優(yōu)點(diǎn):并發(fā)收集绞灼、低停頓(停止用戶線程)利术。但是它有下面三個(gè)明顯的缺點(diǎn):

  • 對(duì) CPU 資源敏感;
  • 無法處理浮動(dòng)垃圾低矮;ps:就是指在之前判斷該對(duì)象不是垃圾印叁,由于用戶線程同時(shí)也是在運(yùn)行過程中的,所以會(huì)導(dǎo)致判斷不準(zhǔn)確的, 可能在判斷完成之后在清除之前這個(gè)對(duì)像已經(jīng)變成了垃圾對(duì)象轮蜕,所以有可能本該此垃圾被回收但是沒有被回收昨悼,只能等待下一次GC再將該對(duì)象回收,所以這種對(duì)像就是浮動(dòng)垃圾)
  • 它使用的回收算法-“標(biāo)記-清除”算法會(huì)導(dǎo)致收集結(jié)束時(shí)會(huì)有大量空間碎片產(chǎn)生跃洛。空間碎片過多時(shí)率触,將會(huì)給大對(duì)象分配帶來很大麻煩,往往出現(xiàn)老年代還有很大空間剩余汇竭,但是無法找到足夠大的連續(xù)空間來分配當(dāng)前對(duì)象葱蝗,不得不提前進(jìn)行一次Full GC。

(4)G1收集器

G1 (Garbage-First) 是一款面向服務(wù)器的垃圾收集器,主要針對(duì)配備多顆處理器及大容量?jī)?nèi)存的機(jī)器. 以極高概率滿足 GC 停頓時(shí)間要求的同時(shí),還具備高吞吐量性能特征.

被視為 JDK1.7 中 HotSpot 虛擬機(jī)的一個(gè)重要進(jìn)化特征细燎。它具備一下特點(diǎn):

  • 并行與并發(fā):G1 能充分利用 CPU两曼、多核環(huán)境下的硬件優(yōu)勢(shì),使用多個(gè) CPU(CPU 或者 CPU 核心)來縮短 Stop-The-World 停頓時(shí)間玻驻。部分其他收集器原本需要停頓 Java 線程執(zhí)行的 GC 動(dòng)作合愈,G1 收集器仍然可以通過并發(fā)的方式讓 java 程序繼續(xù)執(zhí)行。
  • 分代收集:雖然 G1 可以不需要其他收集器配合就能獨(dú)立管理整個(gè) GC 堆击狮,但是還是保留了分代的概念。
  • 空間整合:與 CMS 的“標(biāo)記--清除”算法不同益老,G1 從整體來看是基于“標(biāo)記-整理”算法實(shí)現(xiàn)的收集器彪蓬;從局部上來看是基于“復(fù)制”算法實(shí)現(xiàn)的。
  • 可預(yù)測(cè)的停頓:這是 G1 相對(duì)于 CMS 的另一個(gè)大優(yōu)勢(shì)捺萌,降低停頓時(shí)間是 G1 和 CMS 共同的關(guān)注點(diǎn)档冬,但 G1 除了追求低停頓外,還能建立可預(yù)測(cè)的停頓時(shí)間模型桃纯,能讓使用者明確指定在一個(gè)長(zhǎng)度為 M 毫秒的時(shí)間片段內(nèi)酷誓。

G1 收集器的運(yùn)作大致分為以下幾個(gè)步驟:

  • 初始標(biāo)記
  • 并發(fā)標(biāo)記
  • 最終標(biāo)記
  • 篩選回收

G1 收集器在后臺(tái)維護(hù)了一個(gè)優(yōu)先列表,每次根據(jù)允許的收集時(shí)間态坦,優(yōu)先選擇回收價(jià)值最大的 Region(這也就是它的名字 Garbage-First 的由來)盐数。這種使用 Region 劃分內(nèi)存空間以及有優(yōu)先級(jí)的區(qū)域回收方式,保證了 G1 收集器在有限時(shí)間內(nèi)可以盡可能高的收集效率(把內(nèi)存化整為零)伞梯。

CMS與G1區(qū)別

  • CMS收集器是老年代的收集器玫氢,可以配合新生代的Serial和ParNew收集器一起使用;G1收集器收集范圍是老年代和新生代谜诫,不需要結(jié)合其他收集器使用漾峡;
  • CMS收集器以最小的停頓時(shí)間為目標(biāo)的收集器;G1收集器在最小停頓基礎(chǔ)上可預(yù)測(cè)垃圾回收的停頓時(shí)間喻旷;
  • CMS收集器是使用“標(biāo)記-清除”算法進(jìn)行的垃圾回收生逸,容易產(chǎn)生內(nèi)存碎片;G1收集器使用的是“標(biāo)記-整理”算法,進(jìn)行了空間整合槽袄,降低了內(nèi)存空間碎片烙无。

拓展:標(biāo)記階段算法 - 三色標(biāo)記法

CMS和G1都是使用三色標(biāo)記法,簡(jiǎn)單說能標(biāo)記的都是可用的掰伸,未標(biāo)記的都是垃圾(可回收)皱炉。三色標(biāo)記法:

  • 白色:未標(biāo)記
  • 黑色:本對(duì)象已經(jīng)訪問完了,本對(duì)象所引用的對(duì)象也都訪問完了(都不是垃圾)
  • 灰色:本對(duì)象已經(jīng)訪問完了狮鸭,本對(duì)象所引用的對(duì)象還有沒有全部訪問完合搅。

注意:在STW期間,對(duì)象間的引用不會(huì)發(fā)生變化歧蕉,但是并發(fā)標(biāo)記階段(應(yīng)用程序也在跑)灾部,引用就可能改變、多標(biāo)或漏標(biāo)都可能出現(xiàn)惯退。

(1)多標(biāo) - 浮動(dòng)垃圾

假設(shè)已經(jīng)遍歷到 E(變?yōu)榛疑?赌髓,此時(shí)應(yīng)用執(zhí)行了 objD.fieldE = null (D > E 的引用斷開):

  • 此刻之后,對(duì)象 E/F/G 是“應(yīng)該”被回收的催跪。然而因?yàn)?E 已經(jīng)變?yōu)榛疑怂洌淙詴?huì)被當(dāng)作存活對(duì)象繼續(xù)遍歷下去。最終的結(jié)果是:這部分對(duì)象仍會(huì)被標(biāo)記為存活懊蒸,即本輪 GC 不會(huì)回收這部分內(nèi)存荣倾。
  • 這部分本應(yīng)該回收 但是沒有回收到的內(nèi)存,被稱之為“浮動(dòng)垃圾”骑丸。浮動(dòng)垃圾并不會(huì)影響應(yīng)用程序的正確性舌仍,只是需要等到下一輪垃圾回收中才被清除。
  • 另外通危,針對(duì)并發(fā)標(biāo)記開始后的新對(duì)象铸豁,通常的做法是直接全部當(dāng)成黑色,本輪不會(huì)進(jìn)行清除菊碟。這部分對(duì)象期間可能會(huì)變?yōu)槔诮妫@也算是浮動(dòng)垃圾的一部分。

(2)漏標(biāo) - 內(nèi)存屏障

假設(shè) GC 線程已經(jīng)遍歷到 E(變?yōu)榛疑?框沟,此時(shí)應(yīng)用線程先執(zhí)行了:var G = objE.fieldG; objE.fieldG = null; // 灰色E 斷開引用 白色G objD.fieldG = G; // 黑色D 引用 白色G

  • 此時(shí)切回 GC 線程繼續(xù)跑藏古,因?yàn)?E 已經(jīng)沒有對(duì) G 的引用了,所以不會(huì)將 G 放到灰色集合忍燥;盡管因?yàn)?D 重新引用了 G拧晕,但因?yàn)?D 已經(jīng)是黑色了,不會(huì)再重新做遍歷處理梅垄。
  • 最終導(dǎo)致的結(jié)果是:G 會(huì)一直停留在白色集合中厂捞,最后被當(dāng)作垃圾進(jìn)行清除输玷。這直接影響到了應(yīng)用程序的正確性,是不可接受的靡馁。

不難分析欲鹏,漏標(biāo)只有同時(shí)滿足以下兩個(gè)條件時(shí)才會(huì)發(fā)生:

  • 灰色對(duì)象斷開了白色對(duì)象的引用(直接或間接的引用);即灰色對(duì)象原來成員變量的引用發(fā)生了變化臭墨。
  • 黑色對(duì)象重新引用了該白色對(duì)象赔嚎;即黑色對(duì)象成員變量增加了新的引用。

4.拓展問題(總結(jié))

(1)GC是什么時(shí)候觸發(fā)的

由于對(duì)象進(jìn)行了分代處理(便于內(nèi)存分配與回收)胧弛,因此垃圾回收區(qū)域尤误、時(shí)間也不一樣。GC有兩種類型:Minor GC和Full GC结缚。

  • Minor GC:一般情況下损晤,當(dāng)新對(duì)象生成,并且在Eden申請(qǐng)空間失敗時(shí)红竭,就會(huì)觸發(fā)Minor GC尤勋,對(duì)Eden區(qū)域進(jìn)行GC,清除非存活對(duì)象茵宪,并且把尚且存活的對(duì)象移動(dòng)到Survivor區(qū)最冰。然后整理Survivor的兩個(gè)區(qū)。這種方式的GC是對(duì)年輕代的Eden區(qū)進(jìn)行稀火,不會(huì)影響到年老代锌奴。因?yàn)榇蟛糠謱?duì)象都是從Eden區(qū)開始的,同時(shí)Eden區(qū)不會(huì)分配的很大憾股,所以Eden區(qū)的GC會(huì)頻繁進(jìn)行。因而箕慧,一般在這里需要使用速度快服球、效率高的算法,使Eden去能盡快空閑出來颠焦。
  • Full GC:對(duì)整個(gè)堆進(jìn)行整理斩熊,包括Young、Tenured(老年代)和Perm(永久代)伐庭。Full GC因?yàn)樾枰獙?duì)整個(gè)堆進(jìn)行回收粉渠,所以比Scavenge GC要慢,因此應(yīng)該盡可能減少Full GC的次數(shù)圾另。在對(duì)JVM調(diào)優(yōu)的過程中霸株,很大一部分工作就是對(duì)于Full GC的調(diào)節(jié)。

(2)JVM常見參數(shù)

  • 堆棧配置相關(guān)
-Xmx3550m: 最大堆大小為3550m集乔。
-Xms3550m: 設(shè)置初始堆大小為3550m去件。
-Xmn2g: 設(shè)置年輕代大小為2g。
-Xss128k: 每個(gè)線程的堆棧大小為128k。
-XX:MaxPermSize: 設(shè)置持久代大小為16m
-XX:NewRatio=4: 設(shè)置年輕代(包括Eden和兩個(gè)Survivor區(qū))與年老代的比值(除去持久代)尤溜。
-XX:SurvivorRatio=4: 設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的大小比值倔叼。設(shè)置為4,則兩個(gè)Survivor區(qū)與一個(gè)Eden區(qū)的比值為2:4宫莱,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/6
-XX:MaxTenuringThreshold=0: 設(shè)置垃圾最大年齡丈攒。如果設(shè)置為0的話,則年輕代對(duì)象不經(jīng)過Survivor區(qū)授霸,直接進(jìn)入年老代巡验。默認(rèn)15
  • 垃圾收集器相關(guān)
-XX:+UseParallelGC: 選擇垃圾收集器為并行收集器。
-XX:ParallelGCThreads=20: 配置并行收集器的線程數(shù)
-XX:+UseConcMarkSweepGC: 設(shè)置年老代為并發(fā)收集绝葡。
-XX:CMSFullGCsBeforeCompaction:由于并發(fā)收集器不對(duì)內(nèi)存空間進(jìn)行壓縮深碱、整理,所以運(yùn)行一段時(shí)間以后會(huì)產(chǎn)生“碎片”藏畅,使得運(yùn)行效率降低敷硅。此值設(shè)置運(yùn)行多少次GC以后對(duì)內(nèi)存空間進(jìn)行壓縮、整理愉阎。
-XX:+UseCMSCompactAtFullCollection: 打開對(duì)年老代的壓縮绞蹦。可能會(huì)影響性能榜旦,但是可以消除碎片
  • 輔助信息相關(guān)
-XX:+PrintGC 輸出形式:

[GC 118250K->113543K(130112K), 0.0094143 secs]
[Full GC 121376K->10414K(130112K), 0.0650971 secs]

-XX:+PrintGCDetails 輸出形式:

[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]
[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs

(3)頻繁full gc排查方案

對(duì)于 Full GC 較多的情況幽七,其主要有如下兩個(gè)特征:

  • 線上多個(gè)線程的 CPU 都超過了 100%,通過 jstack 命令可以看到這些線程主要是垃圾回收線程溅呢。
  • 通過 jstat 命令監(jiān)控 GC 情況澡屡,可以看到 Full GC 次數(shù)非常多,并且次數(shù)在不斷增加咐旧。

首先我們可以使用 top 命令查看系統(tǒng) CPU 的占用情況驶鹉。找到占用cpu多的進(jìn)程id, 查看該進(jìn)程的各個(gè)線程運(yùn)行情況。接下來我們可以通過 jstack 命令查看線程id,那個(gè)線程為什么耗費(fèi) CPU 最高铣墨。如果當(dāng)前系統(tǒng)緩慢的原因主要是垃圾回收過于頻繁室埋,導(dǎo)致內(nèi)存溢出。那么接著查看你是哪些對(duì)象導(dǎo)致的內(nèi)存溢出的伊约,這個(gè)可以 Dump 出內(nèi)存日志姚淆,然后通過 Eclipse 的 Mat 工具進(jìn)行查看。經(jīng)過 Mat 工具分析之后屡律,我們基本上就能確定內(nèi)存中主要是哪個(gè)對(duì)象比較消耗內(nèi)存腌逢,然后找到該對(duì)象的創(chuàng)建位置,進(jìn)行處理即可超埋。

(4)jvm調(diào)優(yōu)基本思路

  • 監(jiān)控GC的狀態(tài)上忍。使用各種JVM工具骤肛,查看當(dāng)前日志,分析當(dāng)前JVM參數(shù)設(shè)置窍蓝,并且分析當(dāng)前堆內(nèi)存快照和gc日志腋颠,根據(jù)實(shí)際的各區(qū)域內(nèi)存劃分和GC執(zhí)行時(shí)間,覺得是否進(jìn)行優(yōu)化吓笙。
  • 生成堆的dump文件淑玫。通過JMX的MBean生成當(dāng)前的Heap信息,大小為一個(gè)3G(整個(gè)堆的大忻婢Α)的hprof文件絮蒿,如果沒有啟動(dòng)JMX可以通過Java的jmap命令來生成該文件。
  • 分析dump文件叁鉴。建議使用Eclipse專門的靜態(tài)內(nèi)存分析工具M(jìn)at打開分析土涝。
  • 分析結(jié)果,判斷是否需要優(yōu)化幌墓。如果各項(xiàng)參數(shù)設(shè)置合理但壮,系統(tǒng)沒有超時(shí)日志出現(xiàn),GC頻率不高常侣,GC耗時(shí)不高蜡饵,那么沒有必要進(jìn)行GC優(yōu)化,如果GC時(shí)間超過1-3秒胳施,或者頻繁GC溯祸,則必須優(yōu)化。

(5)故障處理工具

  • jstat:虛擬機(jī)統(tǒng)計(jì)信息監(jiān)視工具舞肆。用于監(jiān)視虛擬機(jī)各種運(yùn)行狀態(tài)信息焦辅。可以顯示本地或遠(yuǎn)程虛擬機(jī)進(jìn)程中的類加載、內(nèi)存椿胯、垃圾收集氨鹏、即時(shí)編譯器等運(yùn)行時(shí)數(shù)據(jù),在沒有 GUI 界面的服務(wù)器上是運(yùn)行期定位虛擬機(jī)性能問題的常用工具压状。
  • jmap:Java 內(nèi)存映像工具。用于生成堆轉(zhuǎn)儲(chǔ)快照跟继,還可以查詢 finalize 執(zhí)行隊(duì)列种冬、Java 堆和方法區(qū)的詳細(xì)信息,如空間使用率嗜历,當(dāng)前使用的是哪種收集器等航夺。
  • jstack:Java 堆棧跟蹤工具阻荒。用于生成虛擬機(jī)當(dāng)前時(shí)刻的線程快照。線程快照就是當(dāng)前虛擬機(jī)內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合十兢,生成線程快照的目的通常是定位線程出現(xiàn)長(zhǎng)時(shí)間停頓的原因趣竣,如線程間死鎖、死循環(huán)旱物、請(qǐng)求外部資源導(dǎo)致的長(zhǎng)時(shí)間掛起等遥缕。

(6)JVM中一次完整的GC流程是怎樣的,對(duì)象如何晉升到老年代

  • Java堆 = 老年代 + 新生代(默認(rèn)1 : 2)宵呛,可以通過參數(shù) –XX:NewRatio 配置单匣。新生代 = Eden + S0 + S1(8 : 1 : 1),以通過參數(shù) –XX:SurvivorRatio 來設(shè)定宝穗。
  • 當(dāng) Eden 區(qū)的空間滿了户秤, Java虛擬機(jī)會(huì)觸發(fā)一次 Minor GC,以收集新生代的垃圾逮矛,存活下來的對(duì)象鸡号,則會(huì)轉(zhuǎn)移到 Survivor區(qū)。
  • 大對(duì)象(需要大量連續(xù)內(nèi)存空間的Java對(duì)象须鼎,如那種很長(zhǎng)的字符串)直接進(jìn)入老年態(tài)鲸伴;
  • 如果對(duì)象在Eden出生,并經(jīng)過第一次Minor GC后仍然存活莉兰,并且被Survivor容納的話挑围,年齡設(shè)為1,每熬過一次Minor GC糖荒,年齡+1杉辙,若年齡超過一定限制(15),則被晉升到老年態(tài)捶朵。即長(zhǎng)期存活的對(duì)象進(jìn)入老年態(tài)蜘矢。(對(duì)應(yīng)虛擬機(jī)參數(shù) -XX:+MaxTenuringThreshold設(shè)定)
  • 老年代滿了而無法容納更多的對(duì)象,Minor GC 之后通常就會(huì)進(jìn)行Full GC综看,Full GC 清理整個(gè)內(nèi)存堆 – 包括年輕代和年老代品腹。
  • Major GC 發(fā)生在老年代的GC,清理老年區(qū)红碑,經(jīng)常會(huì)伴隨至少一次Minor GC舞吭,比Minor GC慢10倍以上。

(7)如果老年代對(duì)象引用年輕代對(duì)象析珊,年輕代對(duì)象是否會(huì)被垃圾回收羡鸥?

(8)逃逸分析

逃逸分析的基本行為就是分析對(duì)象動(dòng)態(tài)作用域:當(dāng)一個(gè)對(duì)象在方法中被定義后,它可能被外部方法所引用忠寻,例如作為調(diào)用參數(shù)傳遞到其他地方中惧浴,稱為方法逃逸。

編譯器會(huì)對(duì)代碼進(jìn)行優(yōu)化:

  • 同步省略奕剃。如果一個(gè)對(duì)象被發(fā)現(xiàn)只能從一個(gè)線程被訪問到衷旅,那么對(duì)于這個(gè)對(duì)象的操作可以不考慮同步捐腿。
  • 將堆分配轉(zhuǎn)化為棧分配。如果一個(gè)對(duì)象在子程序中被分配柿顶,要使指向該對(duì)象的指針永遠(yuǎn)不會(huì)逃逸茄袖,對(duì)象可能是棧分配的候選,而不是堆分配九串。
  • 分離對(duì)象或標(biāo)量替換绞佩。有的對(duì)象可能不需要作為一個(gè)連續(xù)的內(nèi)存結(jié)構(gòu)存在也可以被訪問到,那么對(duì)象的部分(或全部)可以不存儲(chǔ)在內(nèi)存猪钮,而是存儲(chǔ)在CPU寄存器中品山。

5.參考資料

JavaGuide
https://gitee.com/SnailClimb/JavaGuide/blob/master/docs/java/jvm/Java
https://blog.csdn.net/javazejian/article/details/72828483)
http://www.reibang.com/p/e74fe532e35e
https://mp.weixin.qq.com/s/6Laqv1ryS_EobJNOvKcSCA
https://blog.csdn.net/weixin_42510600/article/details/114964301

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市烤低,隨后出現(xiàn)的幾起案子肘交,更是在濱河造成了極大的恐慌,老刑警劉巖扑馁,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涯呻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡腻要,警方通過查閱死者的電腦和手機(jī)复罐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雄家,“玉大人效诅,你說我怎么就攤上這事√思茫” “怎么了乱投?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)顷编。 經(jīng)常有香客問我戚炫,道長(zhǎng),這世上最難降的妖魔是什么媳纬? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任双肤,我火速辦了婚禮,結(jié)果婚禮上钮惠,老公的妹妹穿的比我還像新娘茅糜。我一直安慰自己,他們只是感情好萌腿,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抖苦,像睡著了一般毁菱。 火紅的嫁衣襯著肌膚如雪米死。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天贮庞,我揣著相機(jī)與錄音峦筒,去河邊找鬼。 笑死窗慎,一個(gè)胖子當(dāng)著我的面吹牛物喷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播遮斥,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼峦失,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了术吗?” 一聲冷哼從身側(cè)響起尉辑,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎较屿,沒想到半個(gè)月后隧魄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隘蝎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年购啄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘱么。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡狮含,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拱撵,到底是詐尸還是另有隱情辉川,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布拴测,位于F島的核電站乓旗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏集索。R本人自食惡果不足惜屿愚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望务荆。 院中可真熱鬧妆距,春花似錦、人聲如沸函匕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盅惜。三九已至中剩,卻和暖如春忌穿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背结啼。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工掠剑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人郊愧。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓朴译,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親属铁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子眠寿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容