【注:原文出處 http://blog.csdn.net/zhangerqing】
很多Java面試的時候喳魏,都會問到有關(guān)Java垃圾回收的問題语淘,提到垃圾回收肯定要涉及到JVM內(nèi)存管理機制,Java語言的執(zhí)行效率一直被C巷嚣、C++程序員所嘲笑,其實,事實就是這樣盟广,Java在執(zhí)行效率方面確實很低,一方面瓮钥,Java語言采用面向?qū)ο笏枷虢盍浚@也決定了其必然是開發(fā)效率高,執(zhí)行效率低碉熄。另一方面桨武,Java語言對程序員做了一個美好的承諾:程序員無需去管理內(nèi)存,因為JVM有垃圾回收(GC)锈津,會去自動進(jìn)行垃圾回收呀酸。
其實不然:
1、垃圾回收并不會按照程序員的要求琼梆,隨時進(jìn)行GC性誉。
2、垃圾回收并不會及時的清理內(nèi)存茎杂,盡管有時程序需要額外的內(nèi)存错览。
3、程序員不能對垃圾回收進(jìn)行控制煌往。
因為上面這些事實倾哺,以致我們在寫程序的時候,只能根據(jù)垃圾回收的規(guī)律刽脖,合理安排內(nèi)存羞海,這就要求我們必須徹底了解JVM的內(nèi)存管理機制,這樣才能隨心所欲曾棕,將程序控制于鼓掌之中扣猫!本章系Java之美[從菜鳥到高手演變]系列之JVM內(nèi)存管理及垃圾回收,學(xué)完本章知識翘地,讀者對JVM就會有基本的了解申尤。
本博客永久更新癌幕,如有轉(zhuǎn)載,
請說明出處:http://blog.csdn.net/zhangerqing
如有問題昧穿,請聯(lián)系本人: egg
郵箱:xtfggef@gmail.com
微博:http://weibo.com/xtfggef
一勺远、JVM內(nèi)存的構(gòu)
Java虛擬機會將內(nèi)存分為幾個不同的管理區(qū),這些區(qū)域各自有各自的用途时鸵,根據(jù)不同的特點胶逢,承擔(dān)不同的任務(wù)以及在垃圾回收時運用不同的算法∈吻保總體分為下面幾個部分:
程序計數(shù)器(Program Counter Register)初坠、JVM虛擬機棧(JVM Stacks)、本地方法棧(Native Method Stacks)彭雾、堆(Heap)碟刺、方法區(qū)(Method Area)
如下圖:
1、程序計數(shù)器(Program Counter Register)
這是一塊比較小的內(nèi)存薯酝,不在Ram上半沽,而是直接劃分在CPU上的,程序員無法直接操作它吴菠,它的作用是:JVM在解釋字節(jié)碼文件(.class)時者填,存儲當(dāng)前線程所執(zhí)行的字節(jié)碼的行號,只是一種概念模型做葵,各種JVM所采用的方式不同占哟,字節(jié)碼解釋器工作時,就是通過改變程序計數(shù)器的值來選取下一條要執(zhí)行的指令蜂挪,分支重挑、循環(huán)、跳轉(zhuǎn)棠涮、等基礎(chǔ)功能都是依賴此技術(shù)區(qū)完成的谬哀。還有一種情況,就是我們常說的Java多線程方面的严肪,多線程就是通過現(xiàn)程輪流切換而達(dá)到的史煎,同一時刻,一個內(nèi)核只能執(zhí)行一個指令驳糯,所以篇梭,對于每一個程序來說,必須有一個計數(shù)器來記錄程序的執(zhí)行進(jìn)度酝枢,這樣恬偷,當(dāng)現(xiàn)程恢復(fù)執(zhí)行的時候,才能從正確的地方開始帘睦,所以袍患,每個線程都必須有一個獨立的程序計數(shù)器坦康,這類計數(shù)器為線程私有的內(nèi)存。如果一個線程正在執(zhí)行一個Java方法诡延,則計數(shù)器記錄的是字節(jié)碼的指令的地址滞欠,如果執(zhí)行的一個Native方法,則計數(shù)器的記錄為空肆良,此內(nèi)存區(qū)是唯一一個在Java規(guī)范中沒有任何OutOfMemoryError情況的區(qū)域筛璧。
2、JVM虛擬機棧(JVM Stacks)
JVM虛擬機棧就是我們常說的堆棧的棧(我們常常把內(nèi)存粗略分為堆和棧)惹恃,和程序計數(shù)器一樣夭谤,也是線程私有的,生命周期和線程一樣座舍,每個方法被執(zhí)行的時候會產(chǎn)生一個棧幀沮翔,用于存儲局部變量表、動態(tài)鏈接曲秉、操作數(shù)、方法出口等信息疲牵。方法的執(zhí)行過程就是棧幀在JVM中出棧和入棧的過程承二。局部變量表中存放的是各種基本數(shù)據(jù)類型,如boolean纲爸、byte亥鸠、char、等8種识啦,及引用類型(存放的是指向各個對象的內(nèi)存地址)负蚊,因此,它有一個特點:內(nèi)存空間可以在編譯期間就確定颓哮,運行期不在改變家妆。這個內(nèi)存區(qū)域會有兩種可能的Java異常:StackOverFlowError和OutOfMemoryError。
3冕茅、本地方法棧(Native Method Stacks)
從名字即可看出伤极,本地方法棧就是用來處理Java中的本地方法的,Java類的祖先類Object中有眾多Native方法姨伤,如hashCode()哨坪、wait()等,他們的執(zhí)行很多時候是借助于操作系統(tǒng)乍楚,但是JVM需要對他們做一些規(guī)范当编,來處理他們的執(zhí)行過程。此區(qū)域徒溪,可以有不同的實現(xiàn)方法忿偷,向我們常用的Sun的JVM就是本地方法棧和JVM虛擬機棧是同一個拧篮。
4、堆(Heap)
堆內(nèi)存是內(nèi)存中最重要的一塊牵舱,也是最有必要進(jìn)行深究的一部分串绩。因為Java性能的優(yōu)化,主要就是針對這部分內(nèi)存的芜壁。所有的對象實例及數(shù)組都是在堆上面分配的(隨著JIT技術(shù)的逐漸成熟礁凡,這句話視乎有些絕對,不過至少目前還基本是這樣的)慧妄,可通過-Xmx和-Xms來控制堆的大小顷牌。JIT技術(shù)的發(fā)展產(chǎn)生了新的技術(shù),如棧上分配和標(biāo)量替換塞淹,也許在不久的幾年里窟蓝,即時編譯會誕生及成熟,那個時候饱普,“所有的對象實例及數(shù)組都是在堆上面分配的”這句話就應(yīng)該稍微改改了运挫。堆內(nèi)存是垃圾回收的主要區(qū)域,所以在下文垃圾回收板塊會重點介紹套耕,此處只做概念方面的解釋谁帕。在32位系統(tǒng)上最大為2G,64位系統(tǒng)上無限制冯袍⌒偻冢可通過-Xms和-Xmx控制,-Xms為JVM啟動時申請的最小Heap內(nèi)存康愤,-Xmx為JVM可申請的最大Heap內(nèi)存儡循。
5、方法區(qū)(Method Area)
方法區(qū)是所有線程共享的內(nèi)存區(qū)域征冷,用于存儲已經(jīng)被JVM加載的類信息择膝、常量、靜態(tài)變量等數(shù)據(jù)资盅,一般來說调榄,方法區(qū)屬于持久代(關(guān)于持久代,會在GC部分詳細(xì)介紹呵扛,除了持久代每庆,還有新生代和舊生代),也難怪Java規(guī)范將方法區(qū)描述為堆的一個邏輯部分今穿,但是它不是堆缤灵。方法區(qū)的垃圾回收比較棘手,就算是Sun的HotSpot VM在這方面也沒有做得多么完美。此處引入方法區(qū)中一個重要的概念:運行時常量池腮出。主要用于存放在編譯過程中產(chǎn)生的字面量(字面量簡單理解就是常量)和引用帖鸦。一般情況,常量的內(nèi)存分配在編譯期間就能確定胚嘲,但不一定全是作儿,有一些可能就是運行時也可將常量放入常量池中,如String類中有個Native方法intern()<關(guān)于intern()的詳細(xì)說明馋劈,請看另一篇文章:http://blog.csdn.net/zhangerqing/article/details/8093919>
此處補充一個在JVM內(nèi)存管理之外的一個內(nèi)存區(qū):直接內(nèi)存攻锰。在JDK1.4中新加入類NIO類,引入了一種基于通道與緩沖區(qū)的I/O方式妓雾,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存娶吞,即我們所說的直接內(nèi)存,這樣在某些場景中會提高程序的性能械姻。
二妒蛇、垃圾回收
有句話說的好:Java和C++之間有一堵有內(nèi)存分配和垃圾回收技術(shù)圍成的墻,墻外的人想進(jìn)去楷拳,墻里的人想出去绣夺!這句話的意思,請讀者自己去琢磨唯竹±值迹總的來說,C浸颓、C++程序員有時苦于內(nèi)存泄露,內(nèi)存管理是件令人頭痛的事兒旺拉,但是Java程序員呢产上,又羨慕C++程序員,自己可以控制一切蛾狗,這樣就不會在內(nèi)存管理方面顯得束手無策晋涣,的卻如此,作為Java程序員我們很難去控制JVM的內(nèi)存回收沉桌,只能根據(jù)它的原理去適應(yīng)谢鹊,盡量提高程序的性能。下面開始講解Java垃圾回收留凭,即Garbage Collection,GC佃扼。從以下四個方面進(jìn)行:
1、為什么要進(jìn)行垃圾回收蔼夜?
隨著程序的運行兼耀,內(nèi)存中存在的實例對象、變量等信息占據(jù)的內(nèi)存越來越多,如果不及時進(jìn)行垃圾回收瘤运,必然會帶來程序性能的下降窍霞,甚至?xí)驗榭捎脙?nèi)存不足造成一些不必要的系統(tǒng)異常。
2拯坟、哪些“垃圾”需要回收但金?
在我們上面介紹的五大區(qū)中,有三個是不需要進(jìn)行垃圾回收的:程序計數(shù)器郁季、JVM棧冷溃、本地方法棧。因為它們的生命周期是和線程同步的巩踏,隨著線程的銷毀秃诵,它們占用的內(nèi)存會自動釋放,所以只有方法區(qū)和堆需要進(jìn)行GC塞琼。具體到哪些對象的話菠净,簡單概況一句話:如果某個對象已經(jīng)不存在任何引用,那么它可以被回收彪杉。通俗解釋一下就是說毅往,如果一個對象,已經(jīng)沒有什么作用了派近,就可以被當(dāng)廢棄物被回收了攀唯。
3、什么時候進(jìn)行垃圾回收渴丸?
根據(jù)一個經(jīng)典的引用計數(shù)算法侯嘀,每個對象添加一個引用計數(shù)器,每被引用一次谱轨,計數(shù)器加1戒幔,失去引用,計數(shù)器減1土童,當(dāng)計數(shù)器在一段時間內(nèi)保持為0時诗茎,該對象就認(rèn)為是可以被回收得了。但是献汗,這個算法有明顯的缺陷:當(dāng)兩個對象相互引用敢订,但是二者已經(jīng)沒有作用時,按照常規(guī)罢吃,應(yīng)該對其進(jìn)行垃圾回收楚午,但是其相互引用,又不符合垃圾回收的條件刃麸,因此無法完美處理這塊內(nèi)存清理醒叁,因此Sun的JVM并沒有采用引用計數(shù)算法來進(jìn)行垃圾回收。而是采用一個叫:根搜索算法,如下圖:
基本思想就是:從一個叫GC Roots的對象開始把沼,向下搜索啊易,如果一個對象不能到達(dá)GC Roots對象的時候,說明它已經(jīng)不再被引用饮睬,即可被進(jìn)行垃圾回收(此處 暫且這樣理解租谈,其實事實還有一些不同,當(dāng)一個對象不再被引用時捆愁,并沒有完全“死亡”割去,如果類重寫了finalize()方法,且沒有被系統(tǒng)調(diào)用過昼丑,那么系統(tǒng)會調(diào)用一次finalize()方法呻逆,以完成最后的工作,在這期間菩帝,如果可以將對象重新與任何一個和GC Roots有引用的對象相關(guān)聯(lián)咖城,則該對象可以“重生”,如果不可以呼奢,那么就說明徹底可以被回收了)宜雀,如上圖中的Object5、Object6握础、Object7辐董,雖然它們3個依然可能相互引用,但是總體來說禀综,它們已經(jīng)沒有作用了简烘,這樣就解決了引用計數(shù)算法無法解決的問題。
補充引用的概念:JDK 1.2之后定枷,對引用進(jìn)行了擴充夸研,引入了強、軟依鸥、若、虛四種引用悼沈,被標(biāo)記為這四種引用的對象贱迟,在GC時分別有不同的意義:
a> 強引用(Strong Reference).就是為剛被new出來的對象所加的引用,它的特點就是絮供,永遠(yuǎn)不會被回收衣吠。
b> 軟引用(Soft Reference).聲明為軟引用的類,是可被回收的對象壤靶,如果JVM內(nèi)存并不緊張缚俏,這類對象可以不被回收,如果內(nèi)存緊張,則會被回收忧换。此處有一個問題恬惯,既然被引用為軟引用的對象可以回收,為什么不去回收呢亚茬?其實我們知道酪耳,Java中是存在緩存機制的,就拿字面量緩存來說刹缝,有些時候碗暗,緩存的對象就是當(dāng)前可有可無的,只是留在內(nèi)存中如果還有需要梢夯,則不需要重新分配內(nèi)存即可使用言疗,因此,這些對象即可被引用為軟引用颂砸,方便使用噪奄,提高程序性能。
c> 弱引用(Weak Reference).弱引用的對象就是一定需要進(jìn)行垃圾回收的沾凄,不管內(nèi)存是否緊張梗醇,當(dāng)進(jìn)行GC時,標(biāo)記為弱引用的對象一定會被清理回收撒蟀。
d> 虛引用(Phantom Reference).虛引用弱的可以忽略不計叙谨,JVM完全不會在乎虛引用,其唯一作用就是做一些跟蹤記錄保屯,輔助finalize函數(shù)的使用手负。
最后總結(jié),什么樣的類需要回收呢姑尺?無用的類竟终,何為無用的類?需滿足如下要求:
1> 該類的所有實例對象都已經(jīng)被回收切蟋。
2> 加載該類的ClassLoader已經(jīng)被回收统捶。
3> 該類對應(yīng)的反射類java.lang.Class對象沒有被任何地方引用。
4柄粹、如何進(jìn)行垃圾回收喘鸟?
本塊內(nèi)容以介紹垃圾回收算法為主,因為我們前面有介紹驻右,內(nèi)存主要被分為三塊什黑,新生代、舊生代堪夭、持久代愕把。三代的特點不同拣凹,造就了他們所用的GC算法不同,新生代適合那些生命周期較短恨豁,頻繁創(chuàng)建及銷毀的對象嚣镜,舊生代適合生命周期相對較長的對象,持久代在Sun HotSpot中就是指方法區(qū)(有些JVM中根本就沒有持久代這中說法)圣絮。首先介紹下新生代祈惶、舊生代、持久代的概念及特點:
新生代:New Generation或者Young Generation扮匠。上面大致分為Eden區(qū)和Survivor區(qū)捧请,Survivor區(qū)又分為大小相同的兩部分:FromSpace 和ToSpace。新建的對象都是用新生代分配內(nèi)存棒搜,Eden空間不足的時候疹蛉,會把存活的對象轉(zhuǎn)移到Survivor中,新生代的大小可以由-Xmn來控制力麸,也可以用-XX:SurvivorRatio來控制Eden和Survivor的比例.
舊生代:Old Generation可款。用于存放新生代中經(jīng)過多次垃圾回收仍然存活的對象,例如緩存對象克蚂。舊生代占用大小為-Xmx值減去-Xmn對應(yīng)的值闺鲸。
持久代:Permanent Generation。在Sun的JVM中就是方法區(qū)的意思埃叭,盡管有些JVM大多沒有這一代摸恍。主要存放常量及類的一些信息默認(rèn)最小值為16MB,最大值為64MB赤屋,可通過-XX:PermSize及-XX:MaxPermSize來設(shè)置最小值和最大值立镶。
常見的GC算法:
標(biāo)記-清除算法(Mark-Sweep)
最基礎(chǔ)的GC算法,將需要進(jìn)行回收的對象做標(biāo)記类早,之后掃描媚媒,有標(biāo)記的進(jìn)行回收,這樣就產(chǎn)生兩個步驟:標(biāo)記和清除涩僻。這個算法效率不高缭召,而且在清理完成后會產(chǎn)生內(nèi)存碎片,這樣逆日,如果有大對象需要連續(xù)的內(nèi)存空間時恼琼,還需要進(jìn)行碎片整理,所以屏富,此算法需要改進(jìn)。
復(fù)制算法(Copying)
前面我們談過蛙卤,新生代內(nèi)存分為了三份狠半,Eden區(qū)和2塊Survivor區(qū)噩死,一般Sun的JVM會將Eden區(qū)和Survivor區(qū)的比例調(diào)為8:1,保證有一塊Survivor區(qū)是空閑的神年,這樣已维,在垃圾回收的時候,將不需要進(jìn)行回收的對象放在空閑的Survivor區(qū)已日,然后將Eden區(qū)和第一塊Survivor區(qū)進(jìn)行完全清理垛耳,這樣有一個問題,就是如果第二塊Survivor區(qū)的空間不夠大怎么辦飘千?這個時候堂鲜,就需要當(dāng)Survivor區(qū)不夠用的時候,暫時借持久代的內(nèi)存用一下护奈。此算法適用于新生代缔莲。
標(biāo)記-整理(或叫壓縮)算法(Mark-Compact)
和標(biāo)記-清楚算法前半段一樣,只是在標(biāo)記了不需要進(jìn)行回收的對象后霉旗,將標(biāo)記過的對象移動到一起痴奏,使得內(nèi)存連續(xù),這樣厌秒,只要將標(biāo)記邊界以外的內(nèi)存清理就行了读拆。此算法適用于持久代。
常見的垃圾收集器:
根據(jù)上面說的諸多算法鸵闪,每天JVM都有不同的實現(xiàn)檐晕,我們先來看看常見的一些垃圾收集器:
首先介紹三種實際的垃圾回收器:串行GC(SerialGC)、并行回收GC(Parallel Scavenge)和并行GC(ParNew)。
1、Serial GC髓抑。是最基本评疗、最古老的收集器,但是現(xiàn)在依然被廣泛使用温赔,是一種單線程垃圾回收機制,而且不僅如此,它最大的特點就是在進(jìn)行垃圾回收的時候乃坤,需要將所有正在執(zhí)行的線程暫停(Stop The World),對于有些應(yīng)用這是難以接受的沟蔑,但是我們可以這樣想湿诊,只要我們能夠做到將它所停頓的時間控制在N個毫秒范圍內(nèi),大多數(shù)應(yīng)用我們還是可以接受的瘦材,而且事實是它并沒有讓我們失望厅须,幾十毫米的停頓我們作為客戶機(Client)是完全可以接受的,該收集器適用于單CPU食棕、新生代空間較小及對暫停時間要求不是非常高的應(yīng)用上朗和,是client級別默認(rèn)的GC方式错沽,可以通過-XX:+UseSerialGC來強制指定。
2眶拉、ParNew GC千埃。基本和Serial GC一樣忆植,但本質(zhì)區(qū)別是加入了多線程機制放可,提高了效率,這樣它就可以被用在服務(wù)器端(Server)上朝刊,同時它可以與CMS GC配合耀里,所以,更加有理由將它置于Server端坞古。
3备韧、Parallel Scavenge GC。在整個掃描和復(fù)制過程采用多線程的方式來進(jìn)行痪枫,適用于多CPU织堂、對暫停時間要求較短的應(yīng)用上,是server級別默認(rèn)采用的GC方式奶陈,可用-XX:+UseParallelGC來強制指定易阳,用-XX:ParallelGCThreads=4來指定線程數(shù)。以下給出幾組使用組合:
4吃粒、CMS (Concurrent Mark Sweep)收集器潦俺。該收集器目標(biāo)就是解決Serial GC 的停頓問題,以達(dá)到最短回收時間徐勃。常見的B/S架構(gòu)的應(yīng)用就適合用這種收集器事示,因為其高并發(fā)、高響應(yīng)的特點僻肖。CMS收集器是基于“標(biāo)記-清除”算法實現(xiàn)的肖爵,整個收集過程大致分為4個步驟:
初始標(biāo)記(CMS initial mark)、并發(fā)標(biāo)記(CMS concurrenr mark)臀脏、重新標(biāo)記(CMS remark)劝堪、并發(fā)清除(CMS concurrent sweep)。
其中初始標(biāo)記揉稚、重新標(biāo)記這兩個步驟任然需要停頓其他用戶線程秒啦。初始標(biāo)記僅僅只是標(biāo)記出GC ROOTS能直接關(guān)聯(lián)到的對象,速度很快搀玖,并發(fā)標(biāo)記階段是進(jìn)行GC ROOTS 根搜索算法階段余境,會判定對象是否存活。而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運行而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄葛超,這個階段的停頓時間會被初始標(biāo)記階段稍長暴氏,但比并發(fā)標(biāo)記階段要短。由于整個過程中耗時最長的并發(fā)標(biāo)記和并發(fā)清除過程中绣张,收集器線程都可以與用戶線程一起工作,所以整體來說关带,CMS收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)執(zhí)行的侥涵。
CMS收集器的優(yōu)點:并發(fā)收集、低停頓宋雏,但是CMS還遠(yuǎn)遠(yuǎn)達(dá)不到完美芜飘。
CMS收集器主要有三個顯著缺點:
a>.CMS收集器對CPU資源非常敏感。在并發(fā)階段磨总,雖然不會導(dǎo)致用戶線程停頓嗦明,但是會占用CPU資源而導(dǎo)致引用程序變慢,總吞吐量下降蚪燕。CMS默認(rèn)啟動的回收線程數(shù)是:(CPU數(shù)量+3) / 4娶牌。
b>.CMS收集器無法處理浮動垃圾,可能出現(xiàn)“Concurrent Mode Failure“馆纳,失敗后而導(dǎo)致另一次Full GC的產(chǎn)生诗良。由于CMS并發(fā)清理階段用戶線程還在運行,伴隨程序的運行自熱會有新的垃圾不斷產(chǎn)生鲁驶,這一部分垃圾出現(xiàn)在標(biāo)記過程之后鉴裹,CMS無法在本次收集中處理它們,只好留待下一次GC時將其清理掉钥弯。這一部分垃圾稱為“浮動垃圾”径荔。也是由于在垃圾收集階段用戶線程還需要運行,即需要預(yù)留足夠的內(nèi)存空間給用戶線程使用脆霎,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進(jìn)行收集总处,需要預(yù)留一部分內(nèi)存空間提供并發(fā)收集時的程序運作使用。在默認(rèn)設(shè)置下绪穆,CMS收集器在老年代使用了68%的空間時就會被激活辨泳,也可以通過參數(shù)-XX:CMSInitiatingOccupancyFraction的值來提供觸發(fā)百分比,以降低內(nèi)存回收次數(shù)提高性能玖院。要是CMS運行期間預(yù)留的內(nèi)存無法滿足程序其他線程需要菠红,就會出現(xiàn)“Concurrent Mode Failure”失敗,這時候虛擬機將啟動后備預(yù)案:臨時啟用Serial Old收集器來重新進(jìn)行老年代的垃圾收集难菌,這樣停頓時間就很長了试溯。所以說參數(shù)-XX:CMSInitiatingOccupancyFraction設(shè)置的過高將會很容易導(dǎo)致“Concurrent Mode Failure”失敗,性能反而降低郊酒。
c>.最后一個缺點遇绞,CMS是基于“標(biāo)記-清除”算法實現(xiàn)的收集器键袱,使用“標(biāo)記-清除”算法收集后,會產(chǎn)生大量碎片摹闽√憧В空間碎片太多時,將會給對象分配帶來很多麻煩付鹿,比如說大對象澜汤,內(nèi)存空間找不到連續(xù)的空間來分配不得不提前觸發(fā)一次Full GC。為了解決這個問題舵匾,CMS收集器提供了一個-XX:UseCMSCompactAtFullCollection開關(guān)參數(shù)俊抵,用于在Full GC之后增加一個碎片整理過程,還可通過-XX:CMSFullGCBeforeCompaction參數(shù)設(shè)置執(zhí)行多少次不壓縮的Full GC之后坐梯,跟著來一次碎片整理過程徽诲。
5、G1收集器吵血。相比CMS收集器有不少改進(jìn)谎替,首先基于標(biāo)記-整理算法,不會產(chǎn)生內(nèi)存碎片問題践瓷,其次院喜,可以比較精確的控制停頓,此處不再詳細(xì)介紹晕翠。
6喷舀、Serial Old。Serial Old是Serial收集器的老年代版本淋肾,它同樣使用一個單線程執(zhí)行收集硫麻,使用“標(biāo)記-整理”算法。主要使用在Client模式下的虛擬機樊卓。
7拿愧、Parallel Old。Parallel Old是Parallel Scavenge收集器的老年代版本碌尔,使用多線程和“標(biāo)記-整理”算法浇辜。
8、RTSJ垃圾收集器唾戚,用于Java實時編程柳洋,后續(xù)會補充介紹。
三叹坦、Java程序性能優(yōu)化
gc()的調(diào)用
調(diào)用gc 方法暗示著Java 虛擬機做了一些努力來回收未用對象熊镣,以便能夠快速地重用這些對象當(dāng)前占用的內(nèi)存。當(dāng)控制權(quán)從方法調(diào)用中返回時,虛擬機已經(jīng)盡最大努力從所有丟棄的對象中回收了空間绪囱,調(diào)用System.gc() 等效于調(diào)用Runtime.getRuntime().gc()测蹲。
finalize()的調(diào)用及重寫
gc 只能清除在堆上分配的內(nèi)存(純java語言的所有對象都在堆上使用new分配內(nèi)存),而不能清除棧上分配的內(nèi)存(當(dāng)使用JNI技術(shù)時,可能會在棧上分配內(nèi)存鬼吵,例如java調(diào)用c程序扣甲,而該c程序使用malloc分配內(nèi)存時)。因此齿椅,如果某些對象被分配了棧上的內(nèi)存區(qū)域文捶,那gc就管不著了,對棧上的對象進(jìn)行內(nèi)存回收就要靠finalize()媒咳。舉個例子來說,當(dāng)java 調(diào)用非java方法時(這種方法可能是c或是c++的),在非java代碼內(nèi)部也許調(diào)用了c的malloc()函數(shù)來分配內(nèi)存,而且除非調(diào)用那個了 free() 否則不會釋放內(nèi)存(因為free()是c的函數(shù)),這個時候要進(jìn)行釋放內(nèi)存的工作,gc是不起作用的,因而需要在finalize()內(nèi)部的一個固有方法調(diào)用free()种远。
優(yōu)秀的編程習(xí)慣
(1)避免在循環(huán)體中創(chuàng)建對象涩澡,即使該對象占用內(nèi)存空間不大。
(2)盡量及時使對象符合垃圾回收標(biāo)準(zhǔn)坠敷。
(3)不要采用過深的繼承層次妙同。
(4)訪問本地變量優(yōu)于訪問類中的變量。
本版塊會不斷更新膝迎!
四粥帚、常見問題
1、內(nèi)存溢出
就是你要求分配的java虛擬機內(nèi)存超出了系統(tǒng)能給你的限次,系統(tǒng)不能滿足需求芒涡,于是產(chǎn)生溢出。
2卖漫、內(nèi)存泄漏
是指你向系統(tǒng)申請分配內(nèi)存進(jìn)行使用(new)费尽,可是使用完了以后卻不歸還(delete),結(jié)果你申請到的那塊內(nèi)存你自己也不能再訪問,該塊已分配出來的內(nèi)存也無法再使用羊始,隨著服務(wù)器內(nèi)存的不斷消耗旱幼,而無法使用的內(nèi)存越來越多,系統(tǒng)也不能再次將它分配給需要的程序突委,產(chǎn)生泄露柏卤。一直下去,程序也逐漸無內(nèi)存使用匀油,就會溢出缘缚。
本章內(nèi)容以理論為主,后續(xù)我會不斷地增加一些實際的操作钧唐,如驗證垃圾回收效果忙灼、或者內(nèi)存監(jiān)測什么的,同時也希望讀者會不斷給出指導(dǎo)、建議该园,如有任何問題酸舍,請聯(lián)系:egg:
郵箱:xtfggef@gmail.com
微博:weibo.com/xtfggef
如有轉(zhuǎn)載,請說明出處(http://blog.csdn.net/zhangerqing)里初,謝謝啃勉!
The End