阿里面試官都愛問的內(nèi)存管理和GC算法及回收策略-創(chuàng)新互聯(lián)

JVM內(nèi)存組成結(jié)構(gòu)

JVM棧由堆、棧、本地方法棧、方法區(qū)等部分組成,結(jié)構(gòu)圖如下所示:

創(chuàng)新互聯(lián)建站專注于成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、網(wǎng)頁設(shè)計(jì)、網(wǎng)站制作、網(wǎng)站開發(fā)。公司秉持“客戶至上,用心服務(wù)”的宗旨,從客戶的利益和觀點(diǎn)出發(fā),讓客戶在網(wǎng)絡(luò)營(yíng)銷中找到自己的駐足之地。尊重和關(guān)懷每一位客戶,用嚴(yán)謹(jǐn)?shù)膽B(tài)度對(duì)待客戶,用專業(yè)的服務(wù)創(chuàng)造價(jià)值,成為客戶值得信賴的朋友,為客戶解除后顧之憂。

阿里面試官都愛問的內(nèi)存管理和GC算法及回收策略

JVM內(nèi)存回收

Sun的JVMGenerationalCollecting(垃圾回收)原理是這樣的:把對(duì)象分為年青代(Young)、年老代(Tenured)、持久代(Perm),對(duì)不同生命周期的對(duì)象使用不同的算法。(基于對(duì)對(duì)象生命周期分析)

阿里面試官都愛問的內(nèi)存管理和GC算法及回收策略

1.Young(年輕代)

年輕代分三個(gè)區(qū)。一個(gè)Eden區(qū),兩個(gè)Survivor區(qū)。大部分對(duì)象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時(shí),還存活的對(duì)象將被復(fù)制到Survivor區(qū)(兩個(gè)中的一個(gè)),當(dāng)這個(gè)Survivor區(qū)滿時(shí),此區(qū)的存活對(duì)象將被復(fù)制到另外一個(gè)Survivor區(qū),當(dāng)這個(gè)Survivor去也滿了的時(shí)候,從第一個(gè)Survivor區(qū)復(fù)制過來的并且此時(shí)還存活的對(duì)象,將被復(fù)制年老區(qū)(Tenured。需要注意,Survivor的兩個(gè)區(qū)是對(duì)稱的,沒先后關(guān)系,所以同一個(gè)區(qū)中可能同時(shí)存在從Eden復(fù)制過來對(duì)象,和從前一個(gè)Survivor復(fù)制過來的對(duì)象,而復(fù)制到年老區(qū)的只有從第一個(gè)Survivor去過來的對(duì)象。而且,Survivor區(qū)總有一個(gè)是空的。

2.Tenured(年老代)

年老代存放從年輕代存活的對(duì)象。一般來說年老代存放的都是生命期較長(zhǎng)的對(duì)象。

3.Perm(持久代)

用于存放靜態(tài)文件,如今Java類、方法等。持久代對(duì)垃圾回收沒有顯著影響,但是有些應(yīng)用可能動(dòng)態(tài)生成或者調(diào)用一些class,例如Hibernate等,在這種時(shí)候需要設(shè)置一個(gè)比較大的持久代空間來存放這些運(yùn)行過程中新增的類。持久代大小通過-XX:MaxPermSize=進(jìn)行設(shè)置。

舉個(gè)例子:當(dāng)在程序中生成對(duì)象時(shí),正常對(duì)象會(huì)在年輕代中分配空間,如果是過大的對(duì)象也可能會(huì)直接在年老代生成(據(jù)觀測(cè)在運(yùn)行某程序時(shí)候每次會(huì)生成一個(gè)十兆的空間用收發(fā)消息,這部分內(nèi)存就會(huì)直接在年老代分配)。年輕代在空間被分配完的時(shí)候就會(huì)發(fā)起內(nèi)存回收,大部分內(nèi)存會(huì)被回收,一部分幸存的內(nèi)存會(huì)被拷貝至Survivor的from區(qū),經(jīng)過多次回收以后如果from區(qū)內(nèi)存也分配完畢,就會(huì)也發(fā)生內(nèi)存回收然后將剩余的對(duì)象拷貝至to區(qū)。等到to區(qū)也滿的時(shí)候,就會(huì)再次發(fā)生內(nèi)存回收然后把幸存的對(duì)象拷貝至年老區(qū)。

通常我們說的JVM內(nèi)存回收總是在指堆內(nèi)存回收,確實(shí)只有堆中的內(nèi)容是動(dòng)態(tài)申請(qǐng)分配的,所以以上對(duì)象的年輕代和年老代都是指的JVM的Heap空間,而持久代則是之前提到的MethodArea,不屬于Heap。

關(guān)于JVM內(nèi)存管理的一些建議

  1. 手動(dòng)將生成的無用對(duì)象,中間對(duì)象置為null,加快內(nèi)存回收。

  2. 對(duì)象池技術(shù)如果生成的對(duì)象是可重用的對(duì)象,只是其中的屬性不同時(shí),可以考慮采用對(duì)象池來較少對(duì)象的生成。如果有空閑的對(duì)象就從對(duì)象池中取出使用,沒有再生成新的對(duì)象,大大提高了對(duì)象的復(fù)用率。

  3. JVM調(diào)優(yōu)通過配置JVM的參數(shù)來提高垃圾回收的速度,如果在沒有出現(xiàn)內(nèi)存泄露且上面兩種辦法都不能保證JVM內(nèi)存回收時(shí),可以考慮采用JVM調(diào)優(yōu)的方式來解決,不過一定要經(jīng)過實(shí)體機(jī)的長(zhǎng)期測(cè)試,因?yàn)椴煌膮?shù)可能引起不同的效果。如-Xnoclassgc參數(shù)等。

垃圾對(duì)象的判定

Java堆中存放著幾乎所有的對(duì)象實(shí)例,垃圾收集器對(duì)堆中的對(duì)象進(jìn)行回收前,要先確定這些對(duì)象是否還有用,判定對(duì)象是否為垃圾對(duì)象有如下算法:

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

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

引用計(jì)數(shù)算法的實(shí)現(xiàn)簡(jiǎn)單,判定效率也很高,在大部分情況下它都是一個(gè)不錯(cuò)的選擇,當(dāng)Java語言并沒有選擇這種算法來進(jìn)行垃圾回收,主要原因是它很難解決對(duì)象之間的相互循環(huán)引用問題。

根搜索算法

Java和C#中都是采用根搜索算法來判定對(duì)象是否存活的。這種算法的基本思路是通過一系列名為“GC Roots”的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為引用鏈,當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí),就證明此對(duì)象是不可用的。在Java語言里,可作為GC Roots的兌現(xiàn)包括下面幾種:

  • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象。
  • 方法區(qū)中的類靜態(tài)屬性引用的對(duì)象。
  • 方法區(qū)中的常量引用的對(duì)象。
  • 本地方法棧中JNI(Native方法)的引用對(duì)象。

實(shí)際上,在根搜索算法中,要真正宣告一個(gè)對(duì)象死亡,至少要經(jīng)歷兩次標(biāo)記過程:如果對(duì)象在進(jìn)行根搜索后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈,那它會(huì)被第一次標(biāo)記并且進(jìn)行一次篩選,篩選的條件是此對(duì)象是否有必要執(zhí)行finalize()方法。當(dāng)對(duì)象沒有覆蓋finalize()方法,或finalize()方法已經(jīng)被虛擬機(jī)調(diào)用過,虛擬機(jī)將這兩種情況都視為沒有必要執(zhí)行。如果該對(duì)象被判定為有必要執(zhí)行finalize()方法,那么這個(gè)對(duì)象將會(huì)被放置在一個(gè)名為F-Queue隊(duì)列中,并在稍后由一條由虛擬機(jī)自動(dòng)建立的、低優(yōu)先級(jí)的Finalizer線程去執(zhí)行finalize()方法。finalize()方法是對(duì)象逃脫死亡命運(yùn)的最后一次機(jī)會(huì)(因?yàn)橐粋€(gè)對(duì)象的finalize()方法最多只會(huì)被系統(tǒng)自動(dòng)調(diào)用一次),稍后GC將對(duì)F-Queue中的對(duì)象進(jìn)行第二次小規(guī)模的標(biāo)記,如果要在finalize()方法中成功拯救自己,只要在finalize()方法中讓該對(duì)象重新引用鏈上的任何一個(gè)對(duì)象建立關(guān)聯(lián)即可。而如果對(duì)象這時(shí)還沒有關(guān)聯(lián)到任何鏈上的引用,那它就會(huì)被回收掉。

垃圾收集算法

判定除了垃圾對(duì)象之后,便可以進(jìn)行垃圾回收了。下面介紹一些垃圾收集算法,由于垃圾收集算法的實(shí)現(xiàn)涉及大量的程序細(xì)節(jié),因此這里主要是闡明各算法的實(shí)現(xiàn)思想,而不去細(xì)論算法的具體實(shí)現(xiàn)。

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

標(biāo)記—清除算法是最基礎(chǔ)的收集算法,它分為“標(biāo)記”和“清除”兩個(gè)階段:首先標(biāo)記出所需回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象,它的標(biāo)記過程其實(shí)就是前面的根搜索算法中判定垃圾對(duì)象的標(biāo)記過程。標(biāo)記—清除算法的執(zhí)行情況如下圖所示:

阿里面試官都愛問的內(nèi)存管理和GC算法及回收策略

該算法有如下缺點(diǎn):

  • 標(biāo)記和清除過程的效率都不高。
  • 標(biāo)記清除后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致,當(dāng)程序在以后的運(yùn)行過程中需要分配較大對(duì)象時(shí)無法找到足夠的連續(xù)內(nèi)存而不得不觸發(fā)另一次垃圾收集動(dòng)作。
復(fù)制算法

復(fù)制算法比較適合于新生代,復(fù)制算法是針對(duì)標(biāo)記—清除算法的缺點(diǎn),在其基礎(chǔ)上進(jìn)行改進(jìn)而得到的,它講課用內(nèi)存按容量分為大小相等的兩塊,每次只使用其中的一塊,當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊內(nèi)存上面,然后再把已使用過的內(nèi)存空間一次清理掉。復(fù)制算法有如下優(yōu)點(diǎn):

  • 每次只對(duì)一塊內(nèi)存進(jìn)行回收,運(yùn)行高效。
  • 只需移動(dòng)棧頂指針,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡(jiǎn)單。
  • 內(nèi)存回收時(shí)不用考慮內(nèi)存碎片的出現(xiàn)。

它的缺點(diǎn)是:可一次性分配的大內(nèi)存縮小了一半。
復(fù)制算法的執(zhí)行情況如下圖所示:

阿里面試官都愛問的內(nèi)存管理和GC算法及回收策略

但一般不用按1:1劃分內(nèi)存空間,可以分成一個(gè)大的eden和兩塊小的survivor。

標(biāo)記—整理算法

老年代中,對(duì)象存活率比較高,如果執(zhí)行較多的復(fù)制操作,效率將會(huì)變低,所以老年代一般會(huì)選用其他算法,如標(biāo)記—整理算法。該算法標(biāo)記的過程與標(biāo)記—清除算法中的標(biāo)記過程一樣,但對(duì)標(biāo)記后出的垃圾對(duì)象的處理情況有所不同,它不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。標(biāo)記—整理算法的回收情況如下所示:

阿里面試官都愛問的內(nèi)存管理和GC算法及回收策略

分代收集

當(dāng)前商業(yè)虛擬機(jī)的垃圾收集都采用分代收集來管理內(nèi)存,它根據(jù)對(duì)象的存活周期的不同將內(nèi)存劃分為幾塊,一般是把Java堆分為新生代和老年代。在新生代中,每次垃圾收集時(shí)都會(huì)發(fā)現(xiàn)有大量對(duì)象死去,只有少量存活,因此可選用復(fù)制算法來完成收集,而老年代中因?yàn)閷?duì)象存活率高、沒有額外空間對(duì)它進(jìn)行分配擔(dān)保,就必須使用標(biāo)記—清除算法或標(biāo)記—整理算法來進(jìn)行回收。

每個(gè)對(duì)象都有一個(gè)年齡(Age)計(jì)數(shù)器,如果對(duì)象在Eden出聲并講過一次Minor GC還存活,將被移動(dòng)到Survivor區(qū)并將Age設(shè)置為1,之后每在Survivor區(qū)中熬過一次Minor GC,Age就加1,當(dāng)增加到一定程度(默認(rèn)為15),就可以放到老年代中。

垃圾收集器

垃圾收集器是內(nèi)存回收算法的具體實(shí)現(xiàn),Java虛擬機(jī)規(guī)范中對(duì)垃圾收集器應(yīng)該如何實(shí)現(xiàn)并沒有任何規(guī)定,因此不同廠商、不同版本的虛擬機(jī)所提供的垃圾收集器都可能會(huì)有很大的差別。Sun HotSpot虛擬機(jī)1.6版包含了如下收集器:Serial、ParNew、Parallel Scavenge、CMS、Serial Old、Parallel Old。這些收集器以不同的組合形式配合工作來完成不同分代區(qū)的垃圾收集工作。

垃圾回收分析

在用代碼分析之前,我們對(duì)內(nèi)存的分配策略明確以下三點(diǎn):

  • 對(duì)象優(yōu)先在Eden分配。當(dāng)Eden沒有足夠空間分配時(shí),將發(fā)起一次Minor GC
  • 大對(duì)象(需要大量連續(xù)空間的java對(duì)象,如長(zhǎng)的字符串和數(shù)組)直接進(jìn)入老年代。由于新生代使用復(fù)制算法回收內(nèi)存,這樣可以避免在Eden和兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存復(fù)制。
  • 長(zhǎng)期存活的對(duì)象將進(jìn)入老年代。

對(duì)垃圾回收策略說明以下兩點(diǎn):

  • 新生代GC(Minor GC):發(fā)生在新生代的垃圾收集動(dòng)作,因?yàn)镴ava對(duì)象大多都具有朝生夕滅的特性,因此Minor GC非常頻繁,一般回收速度也比較快。
  • 老年代GC(Major GC/Full GC):發(fā)生在老年代的GC,出現(xiàn)了Major GC,經(jīng)常會(huì)伴隨至少一次Minor GC。由于老年代中的對(duì)象生命周期比較長(zhǎng),因此Major GC并不頻繁,一般都是等待老年代滿了后才進(jìn)行Full GC,而且其速度一般會(huì)比Minor GC慢10倍以上。另外,如果分配了Direct Memory,在老年代中進(jìn)行Full GC時(shí),會(huì)順便清理掉Direct Memory中的廢棄對(duì)象。

Dalvik虛擬機(jī)使用Mark-Sweep算法來進(jìn)行垃圾收集。顧名思義,Mark-Sweep算法就是為Mark和Sweep兩個(gè)階段進(jìn)行垃圾回收。其中,Mark階段從根集(Root Set)開始,遞歸地標(biāo)記出當(dāng)前所有被引用的對(duì)象,而Sweep階段負(fù)責(zé)回收那些沒有被引用的對(duì)象。在分析Dalvik虛擬機(jī)使用的Mark-Sweep算法之前,我們先來了解一下什么情況下會(huì)觸發(fā)GC。

創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國(guó)云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開啟,新人活動(dòng)云服務(wù)器買多久送多久。

當(dāng)前名稱:阿里面試官都愛問的內(nèi)存管理和GC算法及回收策略-創(chuàng)新互聯(lián)
本文鏈接:http://www.bm7419.com/article0/ihsio.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、動(dòng)態(tài)網(wǎng)站App設(shè)計(jì)、面包屑導(dǎo)航云服務(wù)器、定制網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站建設(shè)