免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
JVM調(diào)優(yōu)總結(jié) By 和你在一起@javaeye

真正的好文章~~~~對(duì)GC這一塊講得比較細(xì),圖文并茂~~~~
 
    

JVM調(diào)優(yōu)總結(jié)(一)-- 一些概念

數(shù)據(jù)類型

        Java虛擬機(jī)中,數(shù)據(jù)類型可以分為兩類:基本類型引用類型?;绢愋偷淖兞勘4嬖贾?,即:他代表的值就是數(shù)值本身;而引用類型的變量保存引用值。“引用值”代表了某個(gè)對(duì)象的引用,而不是對(duì)象本身,對(duì)象本身存放在這個(gè)引用值所表示的地址的位置。

       基本類型包括:byte,short,int,long,char,float,double,Boolean,returnAddress

       引用類型包括:類類型,接口類型數(shù)組

堆與棧

       堆和棧是程序運(yùn)行的關(guān)鍵,很有必要把他們的關(guān)系說(shuō)清楚。

                                 

            棧是運(yùn)行時(shí)的單位,而堆是存儲(chǔ)的單位。

         棧解決程序的運(yùn)行問(wèn)題,即程序如何執(zhí)行,或者說(shuō)如何處理數(shù)據(jù);堆解決的是數(shù)據(jù)存儲(chǔ)的問(wèn)題,即數(shù)據(jù)怎么放、放在哪兒。

        在Java中一個(gè)線程就會(huì)相應(yīng)有一個(gè)線程棧與之對(duì)應(yīng),這點(diǎn)很容易理解,因?yàn)椴煌木€程執(zhí)行邏輯有所不同,因此需要一個(gè)獨(dú)立的線程棧。而堆則是所有線程共享的。棧因?yàn)槭沁\(yùn)行單位,因此里面存儲(chǔ)的信息都是跟當(dāng)前線程(或程序)相關(guān)信息的。包括局部變量、程序運(yùn)行狀態(tài)、方法返回值等等;而堆只負(fù)責(zé)存儲(chǔ)對(duì)象信息。

           為什么要把堆和棧區(qū)分出來(lái)呢?棧中不是也可以存儲(chǔ)數(shù)據(jù)嗎?

         第一,從軟件設(shè)計(jì)的角度看,棧代表了處理邏輯,而堆代表了數(shù)據(jù)。這樣分開(kāi),使得處理邏輯更為清晰。分而治之的思想。這種隔離、模塊化的思想在軟件設(shè)計(jì)的方方面面都有體現(xiàn)。

         第二,堆與棧的分離,使得堆中的內(nèi)容可以被多個(gè)棧共享(也可以理解為多個(gè)線程訪問(wèn)同一個(gè)對(duì)象)。這種共享的收益是很多的。一方面這種共享提供了一種有效的數(shù)據(jù)交互方式(如:共享內(nèi)存),另一方面,堆中的共享常量和緩存可以被所有棧訪問(wèn),節(jié)省了空間。

        第三,棧因?yàn)檫\(yùn)行時(shí)的需要,比如保存系統(tǒng)運(yùn)行的上下文,需要進(jìn)行地址段的劃分。由于棧只能向上增長(zhǎng),因此就會(huì)限制住棧存儲(chǔ)內(nèi)容的能力。而堆不同,堆中的對(duì)象是可以根據(jù)需要?jiǎng)討B(tài)增長(zhǎng)的,因此棧和堆的拆分,使得動(dòng)態(tài)增長(zhǎng)成為可能,相應(yīng)棧中只需記錄堆中的一個(gè)地址即可。

        第四,面向?qū)ο缶褪嵌押蜅5耐昝澜Y(jié)合。其實(shí),面向?qū)ο蠓绞降某绦蚺c以前結(jié)構(gòu)化的程序在執(zhí)行上沒(méi)有任何區(qū)別。但是,面向?qū)ο蟮囊?,使得?duì)待問(wèn)題的思考方式發(fā)生了改變,而更接近于自然方式的思考。當(dāng)我們把對(duì)象拆開(kāi),你會(huì)發(fā)現(xiàn),對(duì)象的屬性其實(shí)就是數(shù)據(jù),存放在堆中;而對(duì)象的行為(方法),就是運(yùn)行邏輯,放在棧中。我們?cè)诰帉?xiě)對(duì)象的時(shí)候,其實(shí)即編寫(xiě)了數(shù)據(jù)結(jié)構(gòu),也編寫(xiě)的處理數(shù)據(jù)的邏輯。不得不承認(rèn),面向?qū)ο蟮脑O(shè)計(jì),確實(shí)很美。

             在Java中,Main函數(shù)就是棧的起始點(diǎn),也是程序的起始點(diǎn)

         程序要運(yùn)行總是有一個(gè)起點(diǎn)的。同C語(yǔ)言一樣,java中的Main就是那個(gè)起點(diǎn)。無(wú)論什么java程序,找到main就找到了程序執(zhí)行的入口:)

            堆中存什么?棧中存什么?

        堆中存的是對(duì)象。棧中存的是基本數(shù)據(jù)類型堆中對(duì)象的引用。一個(gè)對(duì)象的大小是不可估計(jì)的,或者說(shuō)是可以動(dòng)態(tài)變化的,但是在棧中,一個(gè)對(duì)象只對(duì)應(yīng)了一個(gè)4btye的引用(堆棧分離的好處:))。

        為什么不把基本類型放堆中呢?因?yàn)槠湔加玫目臻g一般是1~8個(gè)字節(jié)——需要空間比較少,而且因?yàn)槭腔绢愋?,所以不?huì)出現(xiàn)動(dòng)態(tài)增長(zhǎng)的情況——長(zhǎng)度固定,因此棧中存儲(chǔ)就夠了,如果把他存在堆中是沒(méi)有什么意義的(還會(huì)浪費(fèi)空間,后面說(shuō)明)??梢赃@么說(shuō),基本類型和對(duì)象的引用都是存放在棧中,而且都是幾個(gè)字節(jié)的一個(gè)數(shù),因此在程序運(yùn)行時(shí),他們的處理方式是統(tǒng)一的。但是基本類型、對(duì)象引用和對(duì)象本身就有所區(qū)別了,因?yàn)橐粋€(gè)是棧中的數(shù)據(jù)一個(gè)是堆中的數(shù)據(jù)。最常見(jiàn)的一個(gè)問(wèn)題就是,Java中參數(shù)傳遞時(shí)的問(wèn)題。

    Java中的參數(shù)傳遞時(shí)傳值呢?還是傳引用?

        要說(shuō)明這個(gè)問(wèn)題,先要明確兩點(diǎn):

       1. 不要試圖與C進(jìn)行類比,Java中沒(méi)有指針的概念

       2. 程序運(yùn)行永遠(yuǎn)都是在棧中進(jìn)行的,因而參數(shù)傳遞時(shí),只存在傳遞基本類型和對(duì)象引用的問(wèn)題。不會(huì)直接傳對(duì)象本身。

        明確以上兩點(diǎn)后。Java在方法調(diào)用傳遞參數(shù)時(shí),因?yàn)闆](méi)有指針,所以它都是進(jìn)行傳值調(diào)用(這點(diǎn)可以參考C的傳值調(diào)用)。因此,很多書(shū)里面都說(shuō)Java是進(jìn)行傳值調(diào)用,這點(diǎn)沒(méi)有問(wèn)題,而且也簡(jiǎn)化的C中復(fù)雜性。

       但是傳引用的錯(cuò)覺(jué)是如何造成的呢?在運(yùn)行棧中,基本類型和引用的處理是一樣的,都是傳值,所以,如果是傳引用的方法調(diào)用,也同時(shí)可以理解為“傳引用值”的傳值調(diào)用,即引用的處理跟基本類型是完全一樣的。但是當(dāng)進(jìn)入被調(diào)用方法時(shí),被傳遞的這個(gè)引用的值,被程序解釋(或者查找)到堆中的對(duì)象,這個(gè)時(shí)候才對(duì)應(yīng)到真正的對(duì)象。如果此時(shí)進(jìn)行修改,修改的是引用對(duì)應(yīng)的對(duì)象,而不是引用本身,即:修改的是堆中的數(shù)據(jù)。所以這個(gè)修改是可以保持的了。

       對(duì)象,從某種意義上說(shuō),是由基本類型組成的。可以把一個(gè)對(duì)象看作為一棵樹(shù),對(duì)象的屬性如果還是對(duì)象,則還是一顆樹(shù)(即非葉子節(jié)點(diǎn)),基本類型則為樹(shù)的葉子節(jié)點(diǎn)。程序參數(shù)傳遞時(shí),被傳遞的值本身都是不能進(jìn)行修改的,但是,如果這個(gè)值是一個(gè)非葉子節(jié)點(diǎn)(即一個(gè)對(duì)象引用),則可以修改這個(gè)節(jié)點(diǎn)下面的所有內(nèi)容。

       堆和棧中,棧是程序運(yùn)行最根本的東西。程序運(yùn)行可以沒(méi)有堆,但是不能沒(méi)有棧。而堆是為棧進(jìn)行數(shù)據(jù)存儲(chǔ)服務(wù),說(shuō)白了堆就是一塊共享的內(nèi)存。不過(guò),正是因?yàn)槎押蜅5姆蛛x的思想,才使得Java的垃圾回收成為可能。

        Java中,棧的大小通過(guò)-Xss來(lái)設(shè)置,當(dāng)棧中存儲(chǔ)數(shù)據(jù)比較多時(shí),需要適當(dāng)調(diào)大這個(gè)值,否則會(huì)出現(xiàn)java.lang.StackOverflowError異常。常見(jiàn)的出現(xiàn)這個(gè)異常的是無(wú)法返回的遞歸,因?yàn)榇藭r(shí)棧中保存的信息都是方法返回的記錄點(diǎn)。
 

JVM調(diào)優(yōu)總結(jié)(二)-一些概念

Java對(duì)象的大小

         基本數(shù)據(jù)的類型的大小是固定的,這里就不多說(shuō)了。對(duì)于非基本類型的Java對(duì)象,其大小就值得商榷。

         在Java中,一個(gè)空Object對(duì)象的大小是8byte,這個(gè)大小只是保存堆中一個(gè)沒(méi)有任何屬性的對(duì)象的大小??聪旅嬲Z(yǔ)句:

Object ob = new Object();

        這樣在程序中完成了一個(gè)Java對(duì)象的生命,但是它所占的空間為:4byte+8byte。4byte是上面部分所說(shuō)的Java棧中保存引用的所需要的空間。而那8byte則是Java堆中對(duì)象的信息。因?yàn)樗械腏ava非基本類型的對(duì)象都需要默認(rèn)繼承Object對(duì)象,因此不論什么樣的Java對(duì)象,其大小都必須是大于8byte。

   有了Object對(duì)象的大小,我們就可以計(jì)算其他對(duì)象的大小了。

Class NewObject {

    int count;

    boolean flag;

    Object ob;

}

其大小為:空對(duì)象大小(8byte)+int大小(4byte)+Boolean大小(1byte)+空Object引用的大小(4byte)=17byte。但是因?yàn)镴ava在對(duì)對(duì)象內(nèi)存分配時(shí)都是以8的整數(shù)倍來(lái)分,因此大于17byte的最接近8的整數(shù)倍的是24,因此此對(duì)象的大小為24byte。

         這里需要注意一下基本類型的包裝類型的大小。因?yàn)檫@種包裝類型已經(jīng)成為對(duì)象了,因此需要把他們作為對(duì)象來(lái)看待。包裝類型的大小至少是12byte(聲明一個(gè)空Object至少需要的空間),而且12byte沒(méi)有包含任何有效信息,同時(shí),因?yàn)镴ava對(duì)象大小是8的整數(shù)倍,因此一個(gè)基本類型包裝類的大小至少是16byte。這個(gè)內(nèi)存占用是很恐怖的,它是使用基本類型的N倍(N>2),有些類型的內(nèi)存占用更是夸張(隨便想下就知道了)。因此,可能的話應(yīng)盡量少使用包裝類。在JDK5.0以后,因?yàn)榧尤肓俗詣?dòng)類型裝換,因此,Java虛擬機(jī)會(huì)在存儲(chǔ)方面進(jìn)行相應(yīng)的優(yōu)化。

引用類型

        對(duì)象引用類型分為強(qiáng)引用、軟引用、弱引用和虛引用。

        強(qiáng)引用:就是我們一般聲明對(duì)象是時(shí)虛擬機(jī)生成的引用,強(qiáng)引用環(huán)境下,垃圾回收時(shí)需要嚴(yán)格判斷當(dāng)前對(duì)象是否被強(qiáng)引用,如果被強(qiáng)引用,則不會(huì)被垃圾回收

        軟引用:軟引用一般被做為緩存來(lái)使用。與強(qiáng)引用的區(qū)別是,軟引用在垃圾回收時(shí),虛擬機(jī)會(huì)根據(jù)當(dāng)前系統(tǒng)的剩余內(nèi)存來(lái)決定是否對(duì)軟引用進(jìn)行回收。如果剩余內(nèi)存比較緊張,則虛擬機(jī)會(huì)回收軟引用所引用的空間;如果剩余內(nèi)存相對(duì)富裕,則不會(huì)進(jìn)行回收。換句話說(shuō),虛擬機(jī)在發(fā)生OutOfMemory時(shí),肯定是沒(méi)有軟引用存在的。

        弱引用:弱引用與軟引用類似,都是作為緩存來(lái)使用。但與軟引用不同,弱引用在進(jìn)行垃圾回收時(shí),是一定會(huì)被回收掉的,因此其生命周期只存在于一個(gè)垃圾回收周期內(nèi)。

        強(qiáng)引用不用說(shuō),我們系統(tǒng)一般在使用時(shí)都是用的強(qiáng)引用。而“軟引用”和“弱引用”比較少見(jiàn)。他們一般被作為緩存使用,而且一般是在內(nèi)存大小比較受限的情況下做為緩存。因?yàn)槿绻麅?nèi)存足夠大的話,可以直接使用強(qiáng)引用作為緩存即可,同時(shí)可控性更高。因而,他們常見(jiàn)的是被使用在桌面應(yīng)用系統(tǒng)的緩存。
 

JVM調(diào)優(yōu)總結(jié)(三)-基本垃圾回收算法

       可以從不同的的角度去劃分垃圾回收算法:

按照基本回收策略分

引用計(jì)數(shù)(Reference Counting):

        比較古老的回收算法。原理是此對(duì)象有一個(gè)引用,即增加一個(gè)計(jì)數(shù),刪除一個(gè)引用則減少一個(gè)計(jì)數(shù)。垃圾回收時(shí),只用收集計(jì)數(shù)為0的對(duì)象。此算法最致命的是無(wú)法處理循環(huán)引用的問(wèn)題。

標(biāo)記-清除(Mark-Sweep):

        此算法執(zhí)行分兩階段。第一階段從引用根節(jié)點(diǎn)開(kāi)始標(biāo)記所有被引用的對(duì)象,第二階段遍歷整個(gè)堆,把未標(biāo)記的對(duì)象清除。此算法需要暫停整個(gè)應(yīng)用,同時(shí),會(huì)產(chǎn)生內(nèi)存碎片。

復(fù)制(Copying):

        此算法把內(nèi)存空間劃為兩個(gè)相等的區(qū)域,每次只使用其中一個(gè)區(qū)域。垃圾回收時(shí),遍歷當(dāng)前使用區(qū)域,把正在使用中的對(duì)象復(fù)制到另外一個(gè)區(qū)域中。次算法每次只處理正在使用中的對(duì)象,因此復(fù)制成本比較小,同時(shí)復(fù)制過(guò)去以后還能進(jìn)行相應(yīng)的內(nèi)存整理,不會(huì)出現(xiàn)“碎片”問(wèn)題。當(dāng)然,此算法的缺點(diǎn)也是很明顯的,就是需要兩倍內(nèi)存空間。

標(biāo)記-整理(Mark-Compact):

        此算法結(jié)合了“標(biāo)記-清除”和“復(fù)制”兩個(gè)算法的優(yōu)點(diǎn)。也是分兩階段,第一階段從根節(jié)點(diǎn)開(kāi)始標(biāo)記所有被引用對(duì)象,第二階段遍歷整個(gè)堆,把清除未標(biāo)記對(duì)象并且把存活對(duì)象“壓縮”到堆的其中一塊,按順序排放。此算法避免了“標(biāo)記-清除”的碎片問(wèn)題,同時(shí)也避免了“復(fù)制”算法的空間問(wèn)題。

按分區(qū)對(duì)待的方式分

         增量收集(Incremental Collecting):實(shí)時(shí)垃圾回收算法,即:在應(yīng)用進(jìn)行的同時(shí)進(jìn)行垃圾回收。不知道什么原因JDK5.0中的收集器沒(méi)有使用這種算法的。

        分代收集(Generational Collecting):基于對(duì)對(duì)象生命周期分析后得出的垃圾回收算法。把對(duì)象分為年青代、年老代、持久代,對(duì)不同生命周期的對(duì)象使用不同的算法(上述方式中的一個(gè))進(jìn)行回收?,F(xiàn)在的垃圾回收器(從J2SE1.2開(kāi)始)都是使用此算法的。

按系統(tǒng)線程分

         串行收集:串行收集使用單線程處理所有垃圾回收工作,因?yàn)闊o(wú)需多線程交互,實(shí)現(xiàn)容易,而且效率比較高。但是,其局限性也比較明顯,即無(wú)法使用多處理器的優(yōu)勢(shì),所以此收集適合單處理器機(jī)器。當(dāng)然,此收集器也可以用在小數(shù)據(jù)量(100M左右)情況下的多處理器機(jī)器上。

        并行收集:并行收集使用多線程處理垃圾回收工作,因而速度快,效率高。而且理論上CPU數(shù)目越多,越能體現(xiàn)出并行收集器的優(yōu)勢(shì)。

        并發(fā)收集:相對(duì)于串行收集和并行收集而言,前面兩個(gè)在進(jìn)行垃圾回收工作時(shí),需要暫停整個(gè)運(yùn)行環(huán)境,而只有垃圾回收程序在運(yùn)行,因此,系統(tǒng)在垃圾回收時(shí)會(huì)有明顯的暫停,而且暫停時(shí)間會(huì)因?yàn)槎言酱蠖介L(zhǎng)。
 

JVM調(diào)優(yōu)總結(jié)(四)-垃圾回收面臨的問(wèn)題

如何區(qū)分垃圾

        上面說(shuō)到的“引用計(jì)數(shù)”法,通過(guò)統(tǒng)計(jì)控制生成對(duì)象和刪除對(duì)象時(shí)的引用數(shù)來(lái)判斷。垃圾回收程序收集計(jì)數(shù)為0的對(duì)象即可。但是這種方法無(wú)法解決循環(huán)引用。所以,后來(lái)實(shí)現(xiàn)的垃圾判斷算法中,都是從程序運(yùn)行的根節(jié)點(diǎn)出發(fā),遍歷整個(gè)對(duì)象引用,查找存活的對(duì)象。那么在這種方式的實(shí)現(xiàn)中,垃圾回收從哪兒開(kāi)始的呢?即,從哪兒開(kāi)始查找哪些對(duì)象是正在被當(dāng)前系統(tǒng)使用的。上面分析的堆和棧的區(qū)別,其中棧是真正進(jìn)行程序執(zhí)行地方,所以要獲取哪些對(duì)象正在被使用,則需要從Java棧開(kāi)始。同時(shí),一個(gè)棧是與一個(gè)線程對(duì)應(yīng)的,因此,如果有多個(gè)線程的話,則必須對(duì)這些線程對(duì)應(yīng)的所有的棧進(jìn)行檢查。

                                                                    

        同時(shí),除了棧外,還有系統(tǒng)運(yùn)行時(shí)的寄存器等,也是存儲(chǔ)程序運(yùn)行數(shù)據(jù)的。這樣,以棧或寄存器中的引用為起點(diǎn),我們可以找到堆中的對(duì)象,又從這些對(duì)象找到對(duì)堆中其他對(duì)象的引用,這種引用逐步擴(kuò)展,最終以null引用或者基本類型結(jié)束,這樣就形成了一顆以Java棧中引用所對(duì)應(yīng)的對(duì)象為根節(jié)點(diǎn)的一顆對(duì)象樹(shù),如果棧中有多個(gè)引用,則最終會(huì)形成多顆對(duì)象樹(shù)。在這些對(duì)象樹(shù)上的對(duì)象,都是當(dāng)前系統(tǒng)運(yùn)行所需要的對(duì)象,不能被垃圾回收。而其他剩余對(duì)象,則可以視為無(wú)法被引用到的對(duì)象,可以被當(dāng)做垃圾進(jìn)行回收。

        因此,垃圾回收的起點(diǎn)是一些根對(duì)象(java棧, 靜態(tài)變量, 寄存器...)。而最簡(jiǎn)單的Java棧就是Java程序執(zhí)行的main函數(shù)。這種回收方式,也是上面提到的“標(biāo)記-清除”的回收方式

如何處理碎片

         由于不同Java對(duì)象存活時(shí)間是不一定的,因此,在程序運(yùn)行一段時(shí)間以后,如果不進(jìn)行內(nèi)存整理,就會(huì)出現(xiàn)零散的內(nèi)存碎片。碎片最直接的問(wèn)題就是會(huì)導(dǎo)致無(wú)法分配大塊的內(nèi)存空間,以及程序運(yùn)行效率降低。所以,在上面提到的基本垃圾回收算法中,“復(fù)制”方式和“標(biāo)記-整理”方式,都可以解決碎片的問(wèn)題。

如何解決同時(shí)存在的對(duì)象創(chuàng)建和對(duì)象回收問(wèn)題

        垃圾回收線程是回收內(nèi)存的,而程序運(yùn)行線程則是消耗(或分配)內(nèi)存的,一個(gè)回收內(nèi)存,一個(gè)分配內(nèi)存,從這點(diǎn)看,兩者是矛盾的。因此,在現(xiàn)有的垃圾回收方式中,要進(jìn)行垃圾回收前,一般都需要暫停整個(gè)應(yīng)用(即:暫停內(nèi)存的分配),然后進(jìn)行垃圾回收,回收完成后再繼續(xù)應(yīng)用。這種實(shí)現(xiàn)方式是最直接,而且最有效的解決二者矛盾的方式。

        但是這種方式有一個(gè)很明顯的弊端,就是當(dāng)堆空間持續(xù)增大時(shí),垃圾回收的時(shí)間也將會(huì)相應(yīng)的持續(xù)增大,對(duì)應(yīng)應(yīng)用暫停的時(shí)間也會(huì)相應(yīng)的增大。一些對(duì)相應(yīng)時(shí)間要求很高的應(yīng)用,比如最大暫停時(shí)間要求是幾百毫秒,那么當(dāng)堆空間大于幾個(gè)G時(shí),就很有可能超過(guò)這個(gè)限制,在這種情況下,垃圾回收將會(huì)成為系統(tǒng)運(yùn)行的一個(gè)瓶頸。為解決這種矛盾,有了并發(fā)垃圾回收算法,使用這種算法,垃圾回收線程與程序運(yùn)行線程同時(shí)運(yùn)行。在這種方式下,解決了暫停的問(wèn)題,但是因?yàn)樾枰谛律蓪?duì)象的同時(shí)又要回收對(duì)象,算法復(fù)雜性會(huì)大大增加,系統(tǒng)的處理能力也會(huì)相應(yīng)降低,同時(shí),“碎片”問(wèn)題將會(huì)比較難解決。
 

JVM調(diào)優(yōu)總結(jié)(五)-分代垃圾回收詳述1

為什么要分代

        分代的垃圾回收策略,是基于這樣一個(gè)事實(shí):不同的對(duì)象的生命周期是不一樣的。因此,不同生命周期的對(duì)象可以采取不同的收集方式,以便提高回收效率。

        在Java程序運(yùn)行的過(guò)程中,會(huì)產(chǎn)生大量的對(duì)象,其中有些對(duì)象是與業(yè)務(wù)信息相關(guān),比如Http請(qǐng)求中的Session對(duì)象、線程、Socket連接,這類對(duì)象跟業(yè)務(wù)直接掛鉤,因此生命周期比較長(zhǎng)。但是還有一些對(duì)象,主要是程序運(yùn)行過(guò)程中生成的臨時(shí)變量,這些對(duì)象生命周期會(huì)比較短,比如:String對(duì)象,由于其不變類的特性,系統(tǒng)會(huì)產(chǎn)生大量的這些對(duì)象,有些對(duì)象甚至只用一次即可回收。

        試想,在不進(jìn)行對(duì)象存活時(shí)間區(qū)分的情況下,每次垃圾回收都是對(duì)整個(gè)堆空間進(jìn)行回收,花費(fèi)時(shí)間相對(duì)會(huì)長(zhǎng),同時(shí),因?yàn)槊看位厥斩夹枰闅v所有存活對(duì)象,但實(shí)際上,對(duì)于生命周期長(zhǎng)的對(duì)象而言,這種遍歷是沒(méi)有效果的,因?yàn)榭赡苓M(jìn)行了很多次遍歷,但是他們依舊存在。因此,分代垃圾回收采用分治的思想,進(jìn)行代的劃分,把不同生命周期的對(duì)象放在不同代上,不同代上采用最適合它的垃圾回收方式進(jìn)行回收。

如何分代

                                 

如圖所示:

        虛擬機(jī)中的共劃分為三個(gè)代:年輕代(Young Generation)、年老點(diǎn)(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java類的類信息,與垃圾收集要收集的Java對(duì)象關(guān)系不大。年輕代和年老代的劃分是對(duì)垃圾收集影響比較大的。 

年輕代:

        所有新生成的對(duì)象首先都是放在年輕代的。年輕代的目標(biāo)就是盡可能快速的收集掉那些生命周期短的對(duì)象。年輕代分三個(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ù)制過(guò)來(lái)的并且此時(shí)還存活的對(duì)象,將被復(fù)制“年老區(qū)(Tenured)”。需要注意,Survivor的兩個(gè)區(qū)是對(duì)稱的,沒(méi)先后關(guān)系,所以同一個(gè)區(qū)中可能同時(shí)存在從Eden復(fù)制過(guò)來(lái) 對(duì)象,和從前一個(gè)Survivor復(fù)制過(guò)來(lái)的對(duì)象,而復(fù)制到年老區(qū)的只有從第一個(gè)Survivor去過(guò)來(lái)的對(duì)象。而且,Survivor區(qū)總有一個(gè)是空的。同時(shí),根據(jù)程序需要,Survivor區(qū)是可以配置為多個(gè)的(多于兩個(gè)),這樣可以增加對(duì)象在年輕代中的存在時(shí)間,減少被放到年老代的可能。

年老代:

         在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對(duì)象,就會(huì)被放到年老代中。因此,可以認(rèn)為年老代中存放的都是一些生命周期較長(zhǎng)的對(duì)象。

持久代:

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

 

什么情況下觸發(fā)垃圾回收

        由于對(duì)象進(jìn)行了分代處理,因此垃圾回收區(qū)域、時(shí)間也不一樣。GC有兩種類型:Scavenge GCFull GC。

Scavenge GC

        一般情況下,當(dāng)新對(duì)象生成,并且在Eden申請(qǐng)空間失敗時(shí),就會(huì)觸發(fā)Scavenge 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ū)開(kāi)始的,同時(shí)Eden區(qū)不會(huì)分配的很大,所以Eden區(qū)的GC會(huì)頻繁進(jìn)行。因而,一般在這里需要使用速度快、效率高的算法,使Eden去能盡快空閑出來(lái)。

Full GC

        對(duì)整個(gè)堆進(jìn)行整理,包括Young、Tenured和Perm。Full GC因?yàn)樾枰獙?duì)整個(gè)對(duì)進(jìn)行回收,所以比Scavenge GC要慢,因此應(yīng)該盡可能減少Full GC的次數(shù)。在對(duì)JVM調(diào)優(yōu)的過(guò)程中,很大一部分工作就是對(duì)于FullGC的調(diào)節(jié)。有如下原因可能導(dǎo)致Full GC:

      · 年老代(Tenured)被寫(xiě)滿

      · 持久代(Perm)被寫(xiě)滿 

      · System.gc()被顯示調(diào)用 

      ·上一次GC之后Heap的各域分配策略動(dòng)態(tài)變化
 

JVM調(diào)優(yōu)總結(jié)(六)-分代垃圾回收詳述2

分代垃圾回收流程示意

選擇合適的垃圾收集算法

      串行收集器

                     

 

         用單線程處理所有垃圾回收工作,因?yàn)闊o(wú)需多線程交互,所以效率比較高。但是,也無(wú)法使用多處理器的優(yōu)勢(shì),所以此收集器適合單處理器機(jī)器。當(dāng)然,此收集器也可以用在小數(shù)據(jù)量(100M左右)情況下的多處理器機(jī)器上??梢允褂?XX:+UseSerialGC打開(kāi)。

         并行收集器

                     

         對(duì)年輕代進(jìn)行并行垃圾回收,因此可以減少垃圾回收時(shí)間。一般在多線程多處理器機(jī)器上使用。使用-XX:+UseParallelGC.打開(kāi)。并行收集器在J2SE5.0第六6更新上引入,在Java SE6.0中進(jìn)行了增強(qiáng)--可以對(duì)年老代進(jìn)行并行收集。如果年老代不使用并發(fā)收集的話,默認(rèn)是使用單線程進(jìn)行垃圾回收,因此會(huì)制約擴(kuò)展能力。使用-XX:+UseParallelOldGC打開(kāi)。

         使用-XX:ParallelGCThreads=<N>設(shè)置并行垃圾回收的線程數(shù)。此值可以設(shè)置與機(jī)器處理器數(shù)量相等。

此收集器可以進(jìn)行如下配置:

最大垃圾回收暫停:指定垃圾回收時(shí)的最長(zhǎng)暫停時(shí)間,通過(guò)-XX:MaxGCPauseMillis=<N>指定。<N>為毫秒.如果指定了此值的話,堆大小和垃圾回收相關(guān)參數(shù)會(huì)進(jìn)行調(diào)整以達(dá)到指定值。設(shè)定此值可能會(huì)減少應(yīng)用的吞吐量。

吞吐量:吞吐量為垃圾回收時(shí)間與非垃圾回收時(shí)間的比值,通過(guò)-XX:GCTimeRatio=<N>來(lái)設(shè)定,公式為1/(1+N)。例如,-XX:GCTimeRatio=19時(shí),表示5%的時(shí)間用于垃圾回收。默認(rèn)情況為99,即1%的時(shí)間用于垃圾回收。 

并發(fā)收集器

         可以保證大部分工作都并發(fā)進(jìn)行(應(yīng)用不停止),垃圾回收只暫停很少的時(shí)間,此收集器適合對(duì)響應(yīng)時(shí)間要求比較高的中、大規(guī)模應(yīng)用。使用-XX:+UseConcMarkSweepGC打開(kāi)。

                        

         并發(fā)收集器主要減少年老代的暫停時(shí)間,他在應(yīng)用不停止的情況下使用獨(dú)立的垃圾回收線程,跟蹤可達(dá)對(duì)象。在每個(gè)年老代垃圾回收周期中,在收集初期并發(fā)收集器 會(huì)對(duì)整個(gè)應(yīng)用進(jìn)行簡(jiǎn)短的暫停,在收集中還會(huì)再暫停一次。第二次暫停會(huì)比第一次稍長(zhǎng),在此過(guò)程中多個(gè)線程同時(shí)進(jìn)行垃圾回收工作。

         并發(fā)收集器使用處理器換來(lái)短暫的停頓時(shí)間。在一個(gè)N個(gè)處理器的系統(tǒng)上,并發(fā)收集部分使用K/N個(gè)可用處理器進(jìn)行回收,一般情況下1<=K<=N/4。

        在只有一個(gè)處理器的主機(jī)上使用并發(fā)收集器,設(shè)置為incremental mode模式也可獲得較短的停頓時(shí)間。

        浮動(dòng)垃圾:由于在應(yīng)用運(yùn)行的同時(shí)進(jìn)行垃圾回收,所以有些垃圾可能在垃圾回收進(jìn)行完成時(shí)產(chǎn)生,這樣就造成了“Floating Garbage”,這些垃圾需要在下次垃圾回收周期時(shí)才能回收掉。所以,并發(fā)收集器一般需要20%的預(yù)留空間用于這些浮動(dòng)垃圾。

       Concurrent Mode Failure:并發(fā)收集器在應(yīng)用運(yùn)行時(shí)進(jìn)行收集,所以需要保證堆在垃圾回收的這段時(shí)間有足夠的空間供程序使用,否則,垃圾回收還未完成,堆空間先滿了。這種情況下將會(huì)發(fā)生“并發(fā)模式失敗”,此時(shí)整個(gè)應(yīng)用將會(huì)暫停,進(jìn)行垃圾回收。

       啟動(dòng)并發(fā)收集器:因?yàn)椴l(fā)收集在應(yīng)用運(yùn)行時(shí)進(jìn)行收集,所以必須保證收集完成之前有足夠的內(nèi)存空間供程序使用,否則會(huì)出現(xiàn)“Concurrent Mode Failure”。通過(guò)設(shè)置-XX:CMSInitiatingOccupancyFraction=<N>指定還有多少剩余堆時(shí)開(kāi)始執(zhí)行并發(fā)收集

小結(jié)

串行處理器:

--適用情況:數(shù)據(jù)量比較?。?00M左右);單處理器下并且對(duì)響應(yīng)時(shí)間無(wú)要求的應(yīng)用。
--缺點(diǎn):只能用于小型應(yīng)用

并行處理器:

--適用情況:“對(duì)吞吐量有高要求”,多CPU、對(duì)應(yīng)用響應(yīng)時(shí)間無(wú)要求的中、大型應(yīng)用。舉例:后臺(tái)處理、科學(xué)計(jì)算。
--缺點(diǎn):垃圾收集過(guò)程中應(yīng)用響應(yīng)時(shí)間可能加長(zhǎng)

并發(fā)處理器:

--適用情況:“對(duì)響應(yīng)時(shí)間有高要求”,多CPU、對(duì)應(yīng)用響應(yīng)時(shí)間有較高要求的中、大型應(yīng)用。舉例:Web服務(wù)器/應(yīng)用服務(wù)器、電信交換、集成開(kāi)發(fā)環(huán)境。

 

JVM調(diào)優(yōu)總結(jié)(七)-典型配置舉例1

文章分類:Java編程

 

以下配置主要針對(duì)分代垃圾回收算法而言。

 

堆大小設(shè)置

年輕代的設(shè)置很關(guān)鍵

JVM中最大堆大小有三方面限制:相關(guān)操作系統(tǒng)的數(shù)據(jù)模型(32-bt還是64-bit)限制;系統(tǒng)的可用虛擬內(nèi)存限制;系統(tǒng)的可用物理內(nèi)存限制。32位系統(tǒng)下,一般限制在1.5G~2G;64為操作系統(tǒng)對(duì)內(nèi)存無(wú)限制。在Windows Server 2003 系統(tǒng),3.5G物理內(nèi)存,JDK5.0下測(cè)試,最大可設(shè)置為1478m。

典型設(shè)置:

java -Xmx3550m -Xms3550m -Xmn2g –Xss128k

-Xmx3550m:設(shè)置JVM最大可用內(nèi)存為3550M。

-Xms3550m:設(shè)置JVM促使內(nèi)存為3550m。此值可以設(shè)置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內(nèi)存。

-Xmn2g:設(shè)置年輕代大小為2G。整個(gè)堆大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m,所以增大年輕代后,將會(huì)減小年老代大小。此值對(duì)系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8。

-Xss128k:設(shè)置每個(gè)線程的堆棧大小。JDK5.0以后每個(gè)線程堆棧大小為1M,以前每個(gè)線程堆棧大小為256K。更具應(yīng)用的線程所需內(nèi)存大小進(jìn)行調(diào)整。在相同物理內(nèi)存下,減小這個(gè)值能生成更多的線程。但是操作系統(tǒng)對(duì)一個(gè)進(jìn)程內(nèi)的線程數(shù)還是有限制的,不能無(wú)限生成,經(jīng)驗(yàn)值在3000~5000左右。

 

java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

-XX:NewRatio=4:設(shè)置年輕代(包括Eden和兩個(gè)Survivor區(qū))與年老代的比值(除去持久代)。設(shè)置為4,則年輕代與年老代所占比值為1:4,年輕代占整個(gè)堆棧的1/5

-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:MaxPermSize=16m:設(shè)置持久代大小為16m。

-XX:MaxTenuringThreshold=0:設(shè)置垃圾最大年齡。如果設(shè)置為0的話,則年輕代對(duì)象不經(jīng)過(guò)Survivor區(qū),直接進(jìn)入年老代。對(duì)于年老代比較多的應(yīng)用,可以提高效率。如果將此值設(shè)置為一個(gè)較大值,則年輕代對(duì)象會(huì)在Survivor區(qū)進(jìn)行多次復(fù)制,這樣可以增加對(duì)象再年輕代的存活時(shí)間,增加在年輕代即被回收的概論。

 

回收器選擇

JVM給了三種選擇:串行收集器、并行收集器、并發(fā)收集器,但是串行收集器只適用于小數(shù)據(jù)量的情況,所以這里的選擇主要針對(duì)并行收集器和并發(fā)收集器。默認(rèn)情況下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在啟動(dòng)時(shí)加入相應(yīng)參數(shù)。JDK5.0以后,JVM會(huì)根據(jù)當(dāng)前系統(tǒng)配置進(jìn)行判斷。

吞吐量?jī)?yōu)先的并行收集器

如上文所述,并行收集器主要以到達(dá)一定的吞吐量為目標(biāo),適用于科學(xué)技術(shù)和后臺(tái)處理等。

典型配置:

java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

-XX:+UseParallelGC:選擇垃圾收集器為并行收集器。此配置僅對(duì)年輕代有效。即上述配置下,年輕代使用并發(fā)收集,而年老代仍舊使用串行收集。

-XX:ParallelGCThreads=20:配置并行收集器的線程數(shù),即:同時(shí)多少個(gè)線程一起進(jìn)行垃圾回收。此值最好配置與處理器數(shù)目相等。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

-XX:+UseParallelOldGC:配置年老代垃圾收集方式為并行收集。JDK6.0支持對(duì)年老代并行收集。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100

-XX:MaxGCPauseMillis=100:設(shè)置每次年輕代垃圾回收的最長(zhǎng)時(shí)間,如果無(wú)法滿足此時(shí)間,JVM會(huì)自動(dòng)調(diào)整年輕代大小,以滿足此值。

n java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC  -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

-XX:+UseAdaptiveSizePolicy:設(shè)置此選項(xiàng)后,并行收集器會(huì)自動(dòng)選擇年輕代區(qū)大小和相應(yīng)的Survivor區(qū)比例,以達(dá)到目標(biāo)系統(tǒng)規(guī)定的最低相應(yīng)時(shí)間或者收集頻率等,此值建議使用并行收集器時(shí),一直打開(kāi)。

 

響應(yīng)時(shí)間優(yōu)先的并發(fā)收集器

如上文所述,并發(fā)收集器主要是保證系統(tǒng)的響應(yīng)時(shí)間,減少垃圾收集時(shí)的停頓時(shí)間。適用于應(yīng)用服務(wù)器、電信領(lǐng)域等。

典型配置:

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

-XX:+UseConcMarkSweepGC:設(shè)置年老代為并發(fā)收集。測(cè)試中配置這個(gè)以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此時(shí)年輕代大小最好用-Xmn設(shè)置。

-XX:+UseParNewGC: 設(shè)置年輕代為并行收集??膳cCMS收集同時(shí)使用。JDK5.0以上,JVM會(huì)根據(jù)系統(tǒng)配置自行設(shè)置,所以無(wú)需再設(shè)置此值。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

-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:打開(kāi)對(duì)年老代的壓縮??赡軙?huì)影響性能,但是可以消除碎片

 

輔助信息

JVM提供了大量命令行參數(shù),打印信息,供調(diào)試使用。主要有以下一些:

-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]

-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可與上面兩個(gè)混合使用
輸出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]

-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中斷的執(zhí)行時(shí)間。可與上面混合使用。輸出形式:Application time: 0.5291524 seconds

-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期間程序暫停的時(shí)間。可與上面混合使用。輸出形式:Total time for which application threads were stopped: 0.0468229 seconds

-XX:PrintHeapAtGC: 打印GC前后的詳細(xì)堆棧信息。輸出形式:

34.702: [GC {Heap before gc invocations=7:

def new generation   total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)

eden space 49152K,  99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)

from space 6144K,  55% used [0x221d0000, 0x22527e10, 0x227d0000)

to   space 6144K,   0% used [0x21bd0000, 0x21bd0000, 0x221d0000)

tenured generation   total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)

the space 69632K,   3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)

compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)

   the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)

ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)

rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)

34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:

def new generation   total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)

eden space 49152K,   0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)

  from space 6144K,  55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)

  to   space 6144K,   0% used [0x221d0000, 0x221d0000, 0x227d0000)

tenured generation   total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)

the space 69632K,   4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)

compacting perm gen  total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)

   the space 8192K,  35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)

   ro space 8192K,  66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)

   rw space 12288K,  46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)

}

, 0.0757599 secs]

-Xloggc:filename:與上面幾個(gè)配合使用,把相關(guān)日志信息記錄到文件以便分析。
 

JVM調(diào)優(yōu)總結(jié)(八)-典型配置舉例2

文章分類:Java編程

 

常見(jiàn)配置匯總

 

堆設(shè)置

  -Xms:初始堆大小

  -Xmx:最大堆大小

  -XX:NewSize=n:設(shè)置年輕代大小

  -XX:NewRatio=n:設(shè)置年輕代和年老代的比值。如:為3,表示年輕代與年老代比值為1:3,年輕代占整個(gè)年輕代年老代和的1/4

  -XX:SurvivorRatio=n:年輕代中Eden區(qū)與兩個(gè)Survivor區(qū)的比值。注意Survivor區(qū)有兩個(gè)。如:3,表示Eden:Survivor=3:2,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/5

  -XX:MaxPermSize=n:設(shè)置持久代大小

收集器設(shè)置

  -XX:+UseSerialGC:設(shè)置串行收集器

  -XX:+UseParallelGC:設(shè)置并行收集器

  -XX:+UseParalledlOldGC:設(shè)置并行年老代收集器

  -XX:+UseConcMarkSweepGC:設(shè)置并發(fā)收集器

垃圾回收統(tǒng)計(jì)信息

  -XX:+PrintGC

  -XX:+PrintGCDetails

  -XX:+PrintGCTimeStamps

  -Xloggc:filename

并行收集器設(shè)置

  -XX:ParallelGCThreads=n:設(shè)置并行收集器收集時(shí)使用的CPU數(shù)。并行收集線程數(shù)。

  -XX:MaxGCPauseMillis=n:設(shè)置并行收集最大暫停時(shí)間

  -XX:GCTimeRatio=n:設(shè)置垃圾回收時(shí)間占程序運(yùn)行時(shí)間的百分比。公式為1/(1+n)

并發(fā)收集器設(shè)置

  -XX:+CMSIncrementalMode:設(shè)置為增量模式。適用于單CPU情況。

  -XX:ParallelGCThreads=n:設(shè)置并發(fā)收集器年輕代收集方式為并行收集時(shí),使用的CPU數(shù)。并行收集線程數(shù)。

 

調(diào)優(yōu)總結(jié)

年輕代大小選擇

響應(yīng)時(shí)間優(yōu)先的應(yīng)用:盡可能設(shè)大,直到接近系統(tǒng)的最低響應(yīng)時(shí)間限制(根據(jù)實(shí)際情況選擇)。在此種情況下,年輕代收集發(fā)生的頻率也是最小的。同時(shí),減少到達(dá)年老代的對(duì)象。

吞吐量?jī)?yōu)先的應(yīng)用:盡可能的設(shè)置大,可能到達(dá)Gbit的程度。因?yàn)閷?duì)響應(yīng)時(shí)間沒(méi)有要求,垃圾收集可以并行進(jìn)行,一般適合8CPU以上的應(yīng)用。

 

 

年老代大小選擇

 

響應(yīng)時(shí)間優(yōu)先的應(yīng)用:年老代使用并發(fā)收集器,所以其大小需要小心設(shè)置,一般要考慮并發(fā)會(huì)話率會(huì)話持續(xù)時(shí)間等一些參數(shù)。如果堆設(shè)置小了,可以會(huì)造成內(nèi)存碎片、高回收頻率以及應(yīng)用暫停而使用傳統(tǒng)的標(biāo)記清除方式;如果堆大了,則需要較長(zhǎng)的收集時(shí)間。最優(yōu)化的方案,一般需要參考以下數(shù)據(jù)獲得:

  1. 并發(fā)垃圾收集信息

  2. 持久代并發(fā)收集次數(shù)

  3. 傳統(tǒng)GC信息

  4. 花在年輕代和年老代回收上的時(shí)間比例

減少年輕代和年老代花費(fèi)的時(shí)間,一般會(huì)提高應(yīng)用的效率

 

 

吞吐量?jī)?yōu)先的應(yīng)用

一般吞吐量?jī)?yōu)先的應(yīng)用都有一個(gè)很大的年輕代和一個(gè)較小的年老代。原因是,這樣可以盡可能回收掉大部分短期對(duì)象,減少中期的對(duì)象,而年老代盡存放長(zhǎng)期存活對(duì)象。

 

 

較小堆引起的碎片問(wèn)題

因?yàn)槟昀洗牟l(fā)收集器使用標(biāo)記、清除算法,所以不會(huì)對(duì)堆進(jìn)行壓縮。當(dāng)收集器回收時(shí),他會(huì)把相鄰的空間進(jìn)行合并,這樣可以分配給較大的對(duì)象。但是,當(dāng)堆空間較小時(shí),運(yùn)行一段時(shí)間以后,就會(huì)出現(xiàn)“碎片”,如果并發(fā)收集器找不到足夠的空間,那么并發(fā)收集器將會(huì)停止,然后使用傳統(tǒng)的標(biāo)記、清除方式進(jìn)行回收。如果出現(xiàn)“碎片”,可能需要進(jìn)行如下配置:

    1. -XX:+UseCMSCompactAtFullCollection:使用并發(fā)收集器時(shí),開(kāi)啟對(duì)年老代的壓縮。

    2. -XX:CMSFullGCsBeforeCompaction=0:上面配置開(kāi)啟的情況下,這里設(shè)置多少次Full GC后,對(duì)年老代進(jìn)行壓縮
 

JVM調(diào)優(yōu)總結(jié)(九)-新一代的垃圾回收算法

文章分類:Java編程

 

垃圾回收的瓶頸

    傳統(tǒng)分代垃圾回收方式,已經(jīng)在一定程度上把垃圾回收給應(yīng)用帶來(lái)的負(fù)擔(dān)降到了最小,把應(yīng)用的吞吐量推到了一個(gè)極限。但是他無(wú)法解決的一個(gè)問(wèn)題,就是Full GC所帶來(lái)的應(yīng)用暫停。在一些對(duì)實(shí)時(shí)性要求很高的應(yīng)用場(chǎng)景下,GC暫停所帶來(lái)的請(qǐng)求堆積和請(qǐng)求失敗是無(wú)法接受的。這類應(yīng)用可能要求請(qǐng)求的返回時(shí)間在幾百甚至幾十毫秒以內(nèi),如果分代垃圾回收方式要達(dá)到這個(gè)指標(biāo),只能把最大堆的設(shè)置限制在一個(gè)相對(duì)較小范圍內(nèi),但是這樣有限制了應(yīng)用本身的處理能力,同樣也是不可接收的。

    分代垃圾回收方式確實(shí)也考慮了實(shí)時(shí)性要求而提供了并發(fā)回收器,支持最大暫停時(shí)間的設(shè)置,但是受限于分代垃圾回收的內(nèi)存劃分模型,其效果也不是很理想。

    為了達(dá)到實(shí)時(shí)性的要求(其實(shí)Java語(yǔ)言最初的設(shè)計(jì)也是在嵌入式系統(tǒng)上的),一種新垃圾回收方式呼之欲出,它既支持短的暫停時(shí)間,又支持大的內(nèi)存空間分配。可以很好的解決傳統(tǒng)分代方式帶來(lái)的問(wèn)題。

 

 

增量收集的演進(jìn)

    增量收集的方式在理論上可以解決傳統(tǒng)分代方式帶來(lái)的問(wèn)題。增量收集把對(duì)堆空間劃分成一系列內(nèi)存塊,使用時(shí),先使用其中一部分(不會(huì)全部用完),垃圾收集時(shí)把之前用掉的部分中的存活對(duì)象再放到后面沒(méi)有用的空間中,這樣可以實(shí)現(xiàn)一直邊使用邊收集的效果,避免了傳統(tǒng)分代方式整個(gè)使用完了再暫停的回收的情況。

    當(dāng)然,傳統(tǒng)分代收集方式也提供了并發(fā)收集,但是他有一個(gè)很致命的地方,就是把整個(gè)堆做為一個(gè)內(nèi)存塊,這樣一方面會(huì)造成碎片(無(wú)法壓縮),另一方面他的每次收集都是對(duì)整個(gè)堆的收集,無(wú)法進(jìn)行選擇,在暫停時(shí)間的控制上還是很弱。而增量方式,通過(guò)內(nèi)存空間的分塊,恰恰可以解決上面問(wèn)題。

 

 

Garbage Firest(G1)

這部分的內(nèi)容主要參考這里,這篇文章算是對(duì)G1算法論文的解讀。我也沒(méi)加什么東西了。

 

 

目標(biāo)

從設(shè)計(jì)目標(biāo)看G1完全是為了大型應(yīng)用而準(zhǔn)備的。

支持很大的堆

高吞吐量

  --支持多CPU和垃圾回收線程

  --在主線程暫停的情況下,使用并行收集

  --在主線程運(yùn)行的情況下,使用并發(fā)收集

實(shí)時(shí)目標(biāo):可配置在N毫秒內(nèi)最多只占用M毫秒的時(shí)間進(jìn)行垃圾回收

當(dāng)然G1要達(dá)到實(shí)時(shí)性的要求,相對(duì)傳統(tǒng)的分代回收算法,在性能上會(huì)有一些損失。

 

 

算法詳解

    G1可謂博采眾家之長(zhǎng),力求到達(dá)一種完美。他吸取了增量收集優(yōu)點(diǎn),把整個(gè)堆劃分為一個(gè)一個(gè)等大小的區(qū)域(region)。內(nèi)存的回收和劃分都以region為單位;同時(shí),他也吸取了CMS的特點(diǎn),把這個(gè)垃圾回收過(guò)程分為幾個(gè)階段,分散一個(gè)垃圾回收過(guò)程;而且,G1也認(rèn)同分代垃圾回收的思想,認(rèn)為不同對(duì)象的生命周期不同,可以采取不同收集方式,因此,它也支持分代的垃圾回收。為了達(dá)到對(duì)回收時(shí)間的可預(yù)計(jì)性,G1在掃描了region以后,對(duì)其中的活躍對(duì)象的大小進(jìn)行排序,首先會(huì)收集那些活躍對(duì)象小的region,以便快速回收空間(要復(fù)制的活躍對(duì)象少了),因?yàn)榛钴S對(duì)象小,里面可以認(rèn)為多數(shù)都是垃圾,所以這種方式被稱為Garbage First(G1)的垃圾回收算法,即:垃圾優(yōu)先的回收。

 

 

回收步驟:

 

初始標(biāo)記(Initial Marking)

    G1對(duì)于每個(gè)region都保存了兩個(gè)標(biāo)識(shí)用的bitmap,一個(gè)為previous marking bitmap,一個(gè)為next marking bitmap,bitmap中包含了一個(gè)bit的地址信息來(lái)指向?qū)ο蟮钠鹗键c(diǎn)。

    開(kāi)始Initial Marking之前,首先并發(fā)的清空next marking bitmap,然后停止所有應(yīng)用線程,并掃描標(biāo)識(shí)出每個(gè)region中root可直接訪問(wèn)到的對(duì)象,將region中top的值放入next top at mark start(TAMS)中,之后恢復(fù)所有應(yīng)用線程。

    觸發(fā)這個(gè)步驟執(zhí)行的條件為:

    G1定義了一個(gè)JVM Heap大小的百分比的閥值,稱為h,另外還有一個(gè)H,H的值為(1-h)*Heap Size,目前這個(gè)h的值是固定的,后續(xù)G1也許會(huì)將其改為動(dòng)態(tài)的,根據(jù)jvm的運(yùn)行情況來(lái)動(dòng)態(tài)的調(diào)整,在分代方式下,G1還定義了一個(gè)u以及soft limit,soft limit的值為H-u*Heap Size,當(dāng)Heap中使用的內(nèi)存超過(guò)了soft limit值時(shí),就會(huì)在一次clean up執(zhí)行完畢后在應(yīng)用允許的GC暫停時(shí)間范圍內(nèi)盡快的執(zhí)行此步驟;

    在pure方式下,G1將marking與clean up組成一個(gè)環(huán),以便clean up能充分的使用marking的信息,當(dāng)clean up開(kāi)始回收時(shí),首先回收能夠帶來(lái)最多內(nèi)存空間的regions,當(dāng)經(jīng)過(guò)多次的clean up,回收到?jīng)]多少空間的regions時(shí),G1重新初始化一個(gè)新的marking與clean up構(gòu)成的環(huán)。

 

并發(fā)標(biāo)記(Concurrent Marking)

    按照之前Initial Marking掃描到的對(duì)象進(jìn)行遍歷,以識(shí)別這些對(duì)象的下層對(duì)象的活躍狀態(tài),對(duì)于在此期間應(yīng)用線程并發(fā)修改的對(duì)象的以來(lái)關(guān)系則記錄到remembered set logs中,新創(chuàng)建的對(duì)象則放入比top值更高的地址區(qū)間中,這些新創(chuàng)建的對(duì)象默認(rèn)狀態(tài)即為活躍的,同時(shí)修改top值。

 

 

最終標(biāo)記暫停(Final Marking Pause)

    當(dāng)應(yīng)用線程的remembered set logs未滿時(shí),是不會(huì)放入filled RS buffers中的,在這樣的情況下,這些remebered set logs中記錄的card的修改就會(huì)被更新了,因此需要這一步,這一步要做的就是把應(yīng)用線程中存在的remembered set logs的內(nèi)容進(jìn)行處理,并相應(yīng)的修改remembered sets,這一步需要暫停應(yīng)用,并行的運(yùn)行。

 

 

存活對(duì)象計(jì)算及清除(Live Data Counting and Cleanup)

    值得注意的是,在G1中,并不是說(shuō)Final Marking Pause執(zhí)行完了,就肯定執(zhí)行Cleanup這步的,由于這步需要暫停應(yīng)用,G1為了能夠達(dá)到準(zhǔn)實(shí)時(shí)的要求,需要根據(jù)用戶指定的最大的GC造成的暫停時(shí)間來(lái)合理的規(guī)劃什么時(shí)候執(zhí)行Cleanup,另外還有幾種情況也是會(huì)觸發(fā)這個(gè)步驟的執(zhí)行的:

    G1采用的是復(fù)制方法來(lái)進(jìn)行收集,必須保證每次的”to space”的空間都是夠的,因此G1采取的策略是當(dāng)已經(jīng)使用的內(nèi)存空間達(dá)到了H時(shí),就執(zhí)行Cleanup這個(gè)步驟;

    對(duì)于full-young和partially-young的分代模式的G1而言,則還有情況會(huì)觸發(fā)Cleanup的執(zhí)行,full-young模式下,G1根據(jù)應(yīng)用可接受的暫停時(shí)間、回收young regions需要消耗的時(shí)間來(lái)估算出一個(gè)yound regions的數(shù)量值,當(dāng)JVM中分配對(duì)象的young regions的數(shù)量達(dá)到此值時(shí),Cleanup就會(huì)執(zhí)行;partially-young模式下,則會(huì)盡量頻繁的在應(yīng)用可接受的暫停時(shí)間范圍內(nèi)執(zhí)行Cleanup,并最大限度的去執(zhí)行non-young regions的Cleanup。

 

 

展望

    以后JVM的調(diào)優(yōu)或許跟多需要針對(duì)G1算法進(jìn)行調(diào)優(yōu)了。
 
 

JVM調(diào)優(yōu)總結(jié)(十)-調(diào)優(yōu)方法

文章分類:Java編程

JVM調(diào)優(yōu)工具

Jconsole,jProfile,VisualVM

Jconsole : jdk自帶,功能簡(jiǎn)單,但是可以在系統(tǒng)有一定負(fù)荷的情況下使用。對(duì)垃圾回收算法有很詳細(xì)的跟蹤。詳細(xì)說(shuō)明參考這里

 

JProfiler:商業(yè)軟件,需要付費(fèi)。功能強(qiáng)大。詳細(xì)說(shuō)明參考這里

 

VisualVM:JDK自帶,功能強(qiáng)大,與JProfiler類似。推薦。

 

如何調(diào)優(yōu)

觀察內(nèi)存釋放情況、集合類檢查、對(duì)象樹(shù)

上面這些調(diào)優(yōu)工具都提供了強(qiáng)大的功能,但是總的來(lái)說(shuō)一般分為以下幾類功能

 

堆信息查看

 

可查看堆空間大小分配(年輕代、年老代、持久代分配)

提供即時(shí)的垃圾回收功能

垃圾監(jiān)控(長(zhǎng)時(shí)間監(jiān)控回收情況)

 

 

查看堆內(nèi)類、對(duì)象信息查看:數(shù)量、類型等

 

 

對(duì)象引用情況查看

 

有了堆信息查看方面的功能,我們一般可以順利解決以下問(wèn)題:

  --年老代年輕代大小劃分是否合理

  --內(nèi)存泄漏

  --垃圾回收算法設(shè)置是否合理

 

線程監(jiān)控

 

線程信息監(jiān)控:系統(tǒng)線程數(shù)量。

線程狀態(tài)監(jiān)控:各個(gè)線程都處在什么樣的狀態(tài)下

 

 

Dump線程詳細(xì)信息:查看線程內(nèi)部運(yùn)行情況

死鎖檢查

 

熱點(diǎn)分析

 

 

 

    CPU熱點(diǎn):檢查系統(tǒng)哪些方法占用的大量CPU時(shí)間

    內(nèi)存熱點(diǎn):檢查哪些對(duì)象在系統(tǒng)中數(shù)量最大(一定時(shí)間內(nèi)存活對(duì)象和銷(xiāo)毀對(duì)象一起統(tǒng)計(jì))

 

    這兩個(gè)東西對(duì)于系統(tǒng)優(yōu)化很有幫助。我們可以根據(jù)找到的熱點(diǎn),有針對(duì)性的進(jìn)行系統(tǒng)的瓶頸查找和進(jìn)行系統(tǒng)優(yōu)化,而不是漫無(wú)目的的進(jìn)行所有代碼的優(yōu)化。

 

 

快照

    快照是系統(tǒng)運(yùn)行到某一時(shí)刻的一個(gè)定格。在我們進(jìn)行調(diào)優(yōu)的時(shí)候,不可能用眼睛去跟蹤所有系統(tǒng)變化,依賴快照功能,我們就可以進(jìn)行系統(tǒng)兩個(gè)不同運(yùn)行時(shí)刻,對(duì)象(或類、線程等)的不同,以便快速找到問(wèn)題

    舉例說(shuō),我要檢查系統(tǒng)進(jìn)行垃圾回收以后,是否還有該收回的對(duì)象被遺漏下來(lái)的了。那么,我可以在進(jìn)行垃圾回收前后,分別進(jìn)行一次堆情況的快照,然后對(duì)比兩次快照的對(duì)象情況。

 

內(nèi)存泄漏檢查

    內(nèi)存泄漏是比較常見(jiàn)的問(wèn)題,而且解決方法也比較通用,這里可以重點(diǎn)說(shuō)一下,而線程、熱點(diǎn)方面的問(wèn)題則是具體問(wèn)題具體分析了。

    內(nèi)存泄漏一般可以理解為系統(tǒng)資源(各方面的資源,堆、棧、線程等)在錯(cuò)誤使用的情況下,導(dǎo)致使用完畢的資源無(wú)法回收(或沒(méi)有回收),從而導(dǎo)致新的資源分配請(qǐng)求無(wú)法完成,引起系統(tǒng)錯(cuò)誤。

    內(nèi)存泄漏對(duì)系統(tǒng)危害比較大,因?yàn)樗梢灾苯訉?dǎo)致系統(tǒng)的崩潰。

    需要區(qū)別一下,內(nèi)存泄漏和系統(tǒng)超負(fù)荷兩者是有區(qū)別的,雖然可能導(dǎo)致的最終結(jié)果是一樣的。內(nèi)存泄漏是用完的資源沒(méi)有回收引起錯(cuò)誤,而系統(tǒng)超負(fù)荷則是系統(tǒng)確實(shí)沒(méi)有那么多資源可以分配了(其他的資源都在使用)。

 

 

年老代堆空間被占滿

異常: java.lang.OutOfMemoryError: Java heap space

說(shuō)明:

 

    這是最典型的內(nèi)存泄漏方式,簡(jiǎn)單說(shuō)就是所有堆空間都被無(wú)法回收的垃圾對(duì)象占滿,虛擬機(jī)無(wú)法再在分配新空間。

    如上圖所示,這是非常典型的內(nèi)存泄漏的垃圾回收情況圖。所有峰值部分都是一次垃圾回收點(diǎn),所有谷底部分表示是一次垃圾回收后剩余的內(nèi)存。連接所有谷底的點(diǎn),可以發(fā)現(xiàn)一條由底到高的線,這說(shuō)明,隨時(shí)間的推移,系統(tǒng)的堆空間被不斷占滿,最終會(huì)占滿整個(gè)堆空間。因此可以初步認(rèn)為系統(tǒng)內(nèi)部可能有內(nèi)存泄漏。(上面的圖僅供示例,在實(shí)際情況下收集數(shù)據(jù)的時(shí)間需要更長(zhǎng),比如幾個(gè)小時(shí)或者幾天)

 

解決:

    這種方式解決起來(lái)也比較容易,一般就是根據(jù)垃圾回收前后情況對(duì)比,同時(shí)根據(jù)對(duì)象引用情況(常見(jiàn)的集合對(duì)象引用)分析,基本都可以找到泄漏點(diǎn)。

 

 

持久代被占滿

異常:java.lang.OutOfMemoryError: PermGen space

說(shuō)明:

    Perm空間被占滿。無(wú)法為新的class分配存儲(chǔ)空間而引發(fā)的異常。這個(gè)異常以前是沒(méi)有的,但是在Java反射大量使用的今天這個(gè)異常比較常見(jiàn)了。主要原因就是大量動(dòng)態(tài)反射生成的類不斷被加載,最終導(dǎo)致Perm區(qū)被占滿。

    更可怕的是,不同的classLoader即便使用了相同的類,但是都會(huì)對(duì)其進(jìn)行加載,相當(dāng)于同一個(gè)東西,如果有N個(gè)classLoader那么他將會(huì)被加載N次。因此,某些情況下,這個(gè)問(wèn)題基本視為無(wú)解。當(dāng)然,存在大量classLoader和大量反射類的情況其實(shí)也不多。

解決:

    1. -XX:MaxPermSize=16m

    2. 換用JDK。比如JRocket。

 

 

堆棧溢出

異常:java.lang.StackOverflowError

說(shuō)明:這個(gè)就不多說(shuō)了,一般就是遞歸沒(méi)返回,或者循環(huán)調(diào)用造成

 

 

線程堆棧滿

異常:Fatal: Stack size too small

說(shuō)明:java中一個(gè)線程的空間大小是有限制的。JDK5.0以后這個(gè)值是1M。與這個(gè)線程相關(guān)的數(shù)據(jù)將會(huì)保存在其中。但是當(dāng)線程空間滿了以后,將會(huì)出現(xiàn)上面異常。

解決:增加線程棧大小。-Xss2m。但這個(gè)配置無(wú)法解決根本問(wèn)題,還要看代碼部分是否有造成泄漏的部分。

 

系統(tǒng)內(nèi)存被占滿

異常:java.lang.OutOfMemoryError: unable to create new native thread

說(shuō)明

    這個(gè)異常是由于操作系統(tǒng)沒(méi)有足夠的資源來(lái)產(chǎn)生這個(gè)線程造成的。系統(tǒng)創(chuàng)建線程時(shí),除了要在Java堆中分配內(nèi)存外,操作系統(tǒng)本身也需要分配資源來(lái)創(chuàng)建線程。因此,當(dāng)線程數(shù)量大到一定程度以后,堆中或許還有空間,但是操作系統(tǒng)分配不出資源來(lái)了,就出現(xiàn)這個(gè)異常了。

分配給Java虛擬機(jī)的內(nèi)存愈多,系統(tǒng)剩余的資源就越少,因此,當(dāng)系統(tǒng)內(nèi)存固定時(shí),分配給Java虛擬機(jī)的內(nèi)存越多,那么,系統(tǒng)總共能夠產(chǎn)生的線程也就越少,兩者成反比的關(guān)系。同時(shí),可以通過(guò)修改-Xss來(lái)減少分配給單個(gè)線程的空間,也可以增加系統(tǒng)總共內(nèi)生產(chǎn)的線程數(shù)。

解決:

    1. 重新設(shè)計(jì)系統(tǒng)減少線程數(shù)量。

    2. 線程數(shù)量不能減少的情況下,通過(guò)-Xss減小單個(gè)線程大小。以便能生產(chǎn)更多的線程。
 
 

JVM調(diào)優(yōu)總結(jié)(十一)-反思

文章分類:Java編程

垃圾回收的悖論

    所謂“成也蕭何敗蕭何”。Java的垃圾回收確實(shí)帶來(lái)了很多好處,為開(kāi)發(fā)帶來(lái)了便利。但是在一些高性能、高并發(fā)的情況下,垃圾回收確成為了制約Java應(yīng)用的瓶頸。目前JDK的垃圾回收算法,始終無(wú)法解決垃圾回收時(shí)的暫停問(wèn)題,因?yàn)檫@個(gè)暫停嚴(yán)重影響了程序的相應(yīng)時(shí)間,造成擁塞或堆積。這也是后續(xù)JDK增加G1算法的一個(gè)重要原因。

    當(dāng)然,上面是從技術(shù)角度出發(fā)解決垃圾回收帶來(lái)的問(wèn)題,但是從系統(tǒng)設(shè)計(jì)方面我們就需要問(wèn)一下了:

    我們需要分配如此大的內(nèi)存空間給應(yīng)用嗎?

    我們是否能夠通過(guò)有效使用內(nèi)存而不是通過(guò)擴(kuò)大內(nèi)存的方式來(lái)設(shè)計(jì)我們的系統(tǒng)呢?    

我們的內(nèi)存中都放了什么

    內(nèi)存中需要放什么呢?個(gè)人認(rèn)為,內(nèi)存中需要放的是你的應(yīng)用需要在不久的將來(lái)再次用到到的東西。想想看,如果你在將來(lái)不用這些東西,何必放內(nèi)存呢?放文件、數(shù)據(jù)庫(kù)不是更好?這些東西一般包括:

1. 系統(tǒng)運(yùn)行時(shí)業(yè)務(wù)相關(guān)的數(shù)據(jù)。比如web應(yīng)用中的session、即時(shí)消息的session等。這些數(shù)據(jù)一般在一個(gè)用戶訪問(wèn)周期或者一個(gè)使用過(guò)程中都需要存在。

2. 緩存。緩存就比較多了,你所要快速訪問(wèn)的都可以放這里面。其實(shí)上面的業(yè)務(wù)數(shù)據(jù)也可以理解為一種緩存。

3.  線程。

    因此,我們是不是可以這么認(rèn)為,如果我們不把業(yè)務(wù)數(shù)據(jù)和緩存放在JVM中,或者把他們獨(dú)立出來(lái),那么Java應(yīng)用使用時(shí)所需的內(nèi)存將會(huì)大大減少,同時(shí)垃圾回收時(shí)間也會(huì)相應(yīng)減少。

    我認(rèn)為這是可能的。

 

 

解決之道

 

數(shù)據(jù)庫(kù)、文件系統(tǒng)

    把所有數(shù)據(jù)都放入數(shù)據(jù)庫(kù)或者文件系統(tǒng),這是一種最為簡(jiǎn)單的方式。在這種方式下,Java應(yīng)用的內(nèi)存基本上等于處理一次峰值并發(fā)請(qǐng)求所需的內(nèi)存。數(shù)據(jù)的獲取都在每次請(qǐng)求時(shí)從數(shù)據(jù)庫(kù)和文件系統(tǒng)中獲取。也可以理解為,一次業(yè)務(wù)訪問(wèn)以后,所有對(duì)象都可以進(jìn)行回收了。

    這是一種內(nèi)存使用最有效的方式,但是從應(yīng)用角度來(lái)說(shuō),這種方式很低效。

 

 

內(nèi)存-硬盤(pán)映射

    上面的問(wèn)題是因?yàn)槲覀兪褂昧宋募到y(tǒng)帶來(lái)了低效。但是如果我們不是讀寫(xiě)硬盤(pán),而是寫(xiě)內(nèi)存的話效率將會(huì)提高很多。

    數(shù)據(jù)庫(kù)和文件系統(tǒng)都是實(shí)實(shí)在在進(jìn)行了持久化,但是當(dāng)我們并不需要這樣持久化的時(shí)候,我們可以做一些變通——把內(nèi)存當(dāng)硬盤(pán)使。

    內(nèi)存-硬盤(pán)映射很好很強(qiáng)大,既用了緩存又對(duì)Java應(yīng)用的內(nèi)存使用又沒(méi)有影響。Java應(yīng)用還是Java應(yīng)用,他只知道讀寫(xiě)的還是文件,但是實(shí)際上是內(nèi)存。

    這種方式兼得的Java應(yīng)用與緩存兩方面的好處。memcached的廣泛使用也正是這一類的代表。

 

 

同一機(jī)器部署多個(gè)JVM

    這也是一種很好的方式,可以分為縱拆和橫拆??v拆可以理解為把Java應(yīng)用劃分為不同模塊,各個(gè)模塊使用一個(gè)獨(dú)立的Java進(jìn)程。而橫拆則是同樣功能的應(yīng)用部署多個(gè)JVM。

    通過(guò)部署多個(gè)JVM,可以把每個(gè)JVM的內(nèi)存控制一個(gè)垃圾回收可以忍受的范圍內(nèi)即可。但是這相當(dāng)于進(jìn)行了分布式的處理,其額外帶來(lái)的復(fù)雜性也是需要評(píng)估的。另外,也有支持分布式的這種JVM可以考慮,不要要錢(qián)哦:)

 

 

程序控制的對(duì)象生命周期

    這種方式是理想當(dāng)中的方式,目前的虛擬機(jī)還沒(méi)有,純屬假設(shè)。即:考慮由編程方式配置哪些對(duì)象在垃圾收集過(guò)程中可以直接跳過(guò),減少垃圾回收線程遍歷標(biāo)記的時(shí)間。

    這種方式相當(dāng)于在編程的時(shí)候告訴虛擬機(jī)某些對(duì)象你可以在*時(shí)間后在進(jìn)行收集或者由代碼標(biāo)識(shí)可以收集了(類似C、C++),在這之前你即便去遍歷他也是沒(méi)有效果的,他肯定是還在被引用的。

    這種方式如果JVM可以實(shí)現(xiàn),個(gè)人認(rèn)為將是一個(gè)飛躍,Java即有了垃圾回收的優(yōu)勢(shì),又有了C、C++對(duì)內(nèi)存的可控性。

 

 

線程分配

    Java的阻塞式的線程模型基本上可以拋棄了,目前成熟的NIO框架也比較多了。阻塞式IO帶來(lái)的問(wèn)題是線程數(shù)量的線性增長(zhǎng),而NIO則可以轉(zhuǎn)換成為常數(shù)線程。因此,對(duì)于服務(wù)端的應(yīng)用而言,NIO還是唯一選擇。不過(guò),JDK7中為我們帶來(lái)的AIO是否能讓人眼前一亮呢?我們拭目以待。

 

 

其他的JDK

    本文說(shuō)的都是Sun的JDK,目前常見(jiàn)的JDK還有JRocket和IBM的JDK。其中JRocket在IO方面比Sun的高很多,不過(guò)Sun JDK6.0以后提高也很大。而且JRocket在垃圾回收方面,也具有優(yōu)勢(shì),其可設(shè)置垃圾回收的最大暫停時(shí)間也是很吸引人的。不過(guò),系統(tǒng)Sun的G1實(shí)現(xiàn)以后,在這方面會(huì)有一個(gè)質(zhì)的飛躍。
 

JVM調(diào)優(yōu)總結(jié)(十二)-參考資料

文章分類:Java編程

    能整理出上面一些東西,也是因?yàn)檎驹诰奕说募缟?。下面是一些參考資料,供大家學(xué)習(xí),大家有更好的,可以繼續(xù)完善:)

 

· Java 理論與實(shí)踐: 垃圾收集簡(jiǎn)史

 

· Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning

 

· Improving Java Application Performance and Scalability by Reducing Garbage Collection Times and Sizing Memory Using JDK 1.4.1

 

· Hotspot memory management whitepaper

 

· Java Tuning White Paper

 

· Diagnosing a Garbage Collection problem

 

· Java HotSpot VM Options

 

· A Collection of JVM Options

 

· Garbage-First Garbage Collection

 

· Frequently Asked Questions about Garbage Collection in the HotspotTM JavaTM Virtual Machine

· JProfiler試用手記

 

· Java6 JVM參數(shù)選項(xiàng)大全

 

· 《深入Java虛擬機(jī)》。雖然過(guò)去了很多年,但這本書(shū)依舊是經(jīng)典。

 

 

    這里是本系列的最后一篇了,很高興大家能夠喜歡這系列的文章。期間也提了很多問(wèn)題,其中有些是我之前沒(méi)有想到的或者考慮欠妥的,感謝提出這些問(wèn)題的朋友,我也學(xué)到的不少東西。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
性能優(yōu)化之 JVM 高級(jí)特性
7種jvm垃圾回收器,這次全部搞懂
《我想進(jìn)大廠》之JVM奪命連環(huán)10問(wèn)
Java中的垃圾回收機(jī)制
JVM 專題二十一:垃圾回收(五)垃圾回收器 (二)
Java系列筆記(3)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服