国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JVM的基本概念與維護(hù)調(diào)優(yōu)

DevWiki / 1994人閱讀

摘要:棧因?yàn)槭沁\(yùn)行單位,因此里面存儲的信息都是跟當(dāng)前線程相關(guān)的信息。基本類型和對象的引用都是在存放在棧中,而且都是幾個字節(jié)的一個數(shù),因此在程序運(yùn)行時(shí),他們的處理方式是統(tǒng)一的。對象,是由基本類型組成的。

一、概念 數(shù)據(jù)類型

java虛擬機(jī)中,數(shù)據(jù)類型可以分為兩類:

基本類型
引用類型

基本類型的變量保存原始值,即:他代表的值就是數(shù)值本身;而引用類型的變量保存引用值。
基本類型包括:byte,short,int,long,float,double,Boolean

“引用值”代表了某個對象的引用,而不是對象本身,對象本身存放在這個引用值所表示的地址位置。
引用類型包括:類的類型,接口類型和數(shù)組

堆與棧
棧是運(yùn)行時(shí)單位
堆是存儲單位

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

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

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

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

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

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

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

堆中存什么?棧中存什么?
堆中存對象
棧中存最基本數(shù)據(jù)類型和堆中對象的引用

一個對象的大小是不可估計(jì)的,或者說是可估計(jì)的,或者說是可以動態(tài)改變的,但在棧中,一個對象只對應(yīng)4byte的引用

為什么不把基本類型放堆中呢?

因?yàn)槠湔加玫目臻g一般是1~8個字節(jié),需要空間比較少,而且因?yàn)槭腔绢愋停圆粫霈F(xiàn)動態(tài)增長的情況,長度固定,因此棧中存儲就夠了,如果把他存在堆中是沒什么意義的。可以說是浪費(fèi)。基本類型和對象的引用都是在存放在棧中,而且都是幾個字節(jié)的一個數(shù),因此在程序運(yùn)行時(shí),他們的處理方式是統(tǒng)一的。但是基本類型、對象引用和對象本身就是有所區(qū)別的,因?yàn)橐粋€棧中的數(shù)據(jù)一個是堆中的數(shù)據(jù)。最常見的一個問題就是,java中參數(shù)傳遞時(shí)的問題。

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

不要試圖與C進(jìn)行類比,java中沒有指針的概念

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

java在方法調(diào)用傳遞參數(shù)時(shí),因?yàn)闆]有指針,所以它都是進(jìn)行傳值調(diào)用。因此,很多書里面都說java是進(jìn)行傳值調(diào)用,這點(diǎn)沒有問題,而且也簡化了C中的復(fù)雜性。

但是傳引用的錯覺是如何造成的呢?

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

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

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

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

引用類型

對象引用類型分為:

強(qiáng)引用
軟引用
弱引用和虛引用

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

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

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

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

二、基本垃圾回收算法

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

按系統(tǒng)線程分

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

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

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

三、垃圾回收面臨的問題 如何區(qū)分垃圾?

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

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

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

如何處理碎片?

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

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

垃圾回收線程是回收內(nèi)存的,而程序運(yùn)行線程則是消耗(或分配)內(nèi)存的,一個回收內(nèi)存,一個分配內(nèi)存,從這點(diǎn)看,兩者是矛盾的。因此,在現(xiàn)有的垃圾回收方式中,要進(jìn)行垃圾回收前,一般都需要暫停整個應(yīng)用(即暫停內(nèi)存的分配),然后進(jìn)行垃圾回收,回收完成后再繼續(xù)應(yīng)用。這種實(shí)現(xiàn)方式是最直接,而且最有效的解決一者矛盾的方式。但是這種方式有一個很明顯的弊端,就是當(dāng)堆空間持續(xù)增大時(shí),垃圾回收的時(shí)間也將會相應(yīng)的持續(xù)增大,對應(yīng)應(yīng)用暫停的時(shí)間也會相應(yīng)的增大。一些對相應(yīng)時(shí)間要求很高的應(yīng)用,比如最大暫停時(shí)間要求是幾百毫秒,那么當(dāng)堆空間大于幾個G時(shí),就很有可能超過這個限制,在這種情況下,垃圾收將會成為系統(tǒng)運(yùn)行的一個瓶頸。為解決這種矛盾,有了并發(fā)垃圾回收算法,使用這種算法,垃圾回收線程與程序運(yùn)行線程同時(shí)運(yùn)行。在這種方式下,解決了暫停的問題,但是因?yàn)樾枰?b>新生成對象的同時(shí)又要回收對象,算法復(fù)雜性會大大增加,系統(tǒng)的處理能力也會相應(yīng)降低,同時(shí),“碎片”問題將會比較難解決

四、分代垃圾回收詳述 為什么要分代?

分代的垃圾回收策略,是基于這樣一個事實(shí):不同的對象的生命周期是不一樣的。因此,不同生命周期的對象可以采取不同的收集方式,以便提高回收效率。在Java程序運(yùn)行的過程中,會產(chǎn)生大量的對象,其中有些對象是與業(yè)務(wù)信息相關(guān),比如Http?請求中的Session對象、線程、Socket連接,這類對象跟業(yè)務(wù)直接掛鉤,因此生命的周期比較長,但是還有一些對象,主要是程序運(yùn)行過程中生成的臨時(shí)變量,這些對象生命周期會比較短,比如:String對象,由于其不變類的特性,系統(tǒng)會產(chǎn)生大量的這些對象,有些對象甚至只用一次即可回收。

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

虛擬機(jī)中的共劃分為三個代:

年輕代(Young?Generation)
年老代(Old?Generation)
持久代(Permanent?Generation)

其中持久代主要存放的是Java類的類信息,與垃圾收集要收集的Java對象關(guān)系不大。年輕代和年老代的劃分是對垃圾收集影響比較大的。

年輕代:所有新生成的對象首先都是放在年輕代的。年輕代的目標(biāo)就是盡可能快速的收集掉那些生命周期短的對象。年輕代分三個區(qū):1個Eden區(qū),2個Survivor區(qū)(一般而言)。大部分對象在Eden區(qū)中生成,當(dāng)Eden區(qū)滿時(shí),還存活的對象將被復(fù)制到Survivor區(qū)(兩個中的一個),當(dāng)這個Survivor區(qū)滿時(shí),此區(qū)的存活對象將被復(fù)制到另外一個Survivor區(qū),當(dāng)這個Survivor區(qū)也滿了的時(shí)候,從第一個Survivor區(qū)復(fù)制過來的并且此時(shí)還存活的對象將被復(fù)制“年老區(qū)(Tenured)”。需要注意,Survivor的兩個區(qū)是對稱的,沒先后關(guān)系,所以同一個區(qū)中可能同時(shí)存在從Eden復(fù)制過來對象,和從前一個Survivor復(fù)制過來的對象,而復(fù)制到年老區(qū)的只有從第一個Survivor區(qū)過來的對象。而且,Survivor區(qū)總有一個是空的。同時(shí),根據(jù)程序需要,Survivor區(qū)是可以配置多個的,這樣可以增加對象在年輕代中存在的時(shí)間,減少被放到老代的可能。

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

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

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

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

Scavenge?GC
Full?Gc

Scavenge?GC:一般情況下,當(dāng)新對象生成,并且在Eden申請空間失敗時(shí),就會觸發(fā)?Scavenge?GC,對Eden區(qū)域進(jìn)行GC,清除非存活對象,并且把尚且存活的對象移動到Survivor區(qū),然后整理Survivor的兩個區(qū)。這種方式的GC是對年輕代的Eden區(qū)進(jìn)行,不會影響到年老代。因?yàn)榇蟛糠謱ο蠖际菑腅den區(qū)開始的,同時(shí)Eden區(qū)不會分配的很大,所以Eden區(qū)的GC會須繁進(jìn)行。因而,一般在這里需要使用速度快、效率高的算法,使Eden去能盡快空閑出來。

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

年老代(Tenured)被寫滿

持久代(Perm)被寫滿

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

上一次GC之后Heap的個域分配策略動態(tài)變化

選擇合適的垃圾收集算法

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

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

此收集器可以進(jìn)行如下配置:
最大垃圾回收暫停:指定垃圾回的時(shí)的最長暫停時(shí)間,通討-XX:MaxGCPauseMilis=指定。為毫秒,如果指定了此值的話,堆大小和垃圾回收相關(guān)參數(shù)會進(jìn)行調(diào)整以達(dá)到指定值。設(shè)定此值可能會減少應(yīng)用的吞吐量。
吞吐量:吞吐量為垃圾回收時(shí)間與非垃圾回收時(shí)間的比值,通過-XX:GCTimeRatio=來設(shè)定,公式為1/(1+N)。例如,-XX:GCTimeRatio=19時(shí),表示5%的時(shí)間用于垃圾回收。默認(rèn)情況為99,即1%的時(shí)間用于垃圾回收。

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

并發(fā)收集器主要減少年老代的暫停時(shí)間,他在應(yīng)用不停止的情況下使用獨(dú)立的垃圾回收線程,跟蹤可達(dá)對象。在每個年老代垃圾回收周期中,在收集初期并發(fā)收集器會對整個應(yīng)用進(jìn)行簡短的暫停,在收集中還會再暫停一次。第二次暫停會比第一次稍長,在此過程中多個線程同時(shí)進(jìn)行垃圾回收工作。并發(fā)收集器使用處理器短暫的停頓時(shí)間。在一個N個處理器的系統(tǒng)上,并發(fā)收集部分使用KWN個可用處理器進(jìn)行回收,一般情況下1<=K<=N/4。在只有一個處理器的主機(jī)上使用并發(fā)收集器,設(shè)置為incrementalmode?模式也可獲得較短的停頓時(shí)間。

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

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

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

小結(jié)

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

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

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

五、常見配置匯總 堆設(shè)置

-Xms:初始堆大。
-Xmx:最大堆大小。
初始堆大小與最大堆大小盡量小于4G。兩值不相等的話,內(nèi)存地址中會計(jì)算會對應(yīng)用程序產(chǎn)生影響,索性設(shè)為相等。

-XX:NewSize=n:設(shè)置年輕代大小。
-XX:NewRatio=n:設(shè)置年輕代和年老代的比值。如:為3,表示年輕代與年老代比值為1:3,年輕代占整個年輕代年老代的1/4。
-XX:SurvivorRatio=n:年輕代中Eden區(qū)與連個Survivor區(qū)的比值。注意Survivor區(qū)有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區(qū)占整個年代的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:輸出GC日志。
-XX:+PrintGCDetails:輸出GC的詳細(xì)日志。
-XX:+PrintGCTimeStamps:輸出GC的時(shí)間戳(以基準(zhǔn)時(shí)間的形式)。
-Xloggc:filename:日志文件的輸出路徑。

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

-XX:ParallelGCThreads=n:設(shè)置并行收集器收集使用的CPU數(shù)。并行收集線程數(shù)。
-XX:MaxGCPauseMillis=n:設(shè)置并行收集最大暫停時(shí)間。
-XX:GCTimeRatio=n:設(shè)置垃圾回收時(shí)間占程序運(yùn)行時(shí)間的百分比。公式1/(1+n)。
-XX:+CMSlncrementalMode:設(shè)置為增量模式。適用于單CPU情況。
-XX:ParallelGCThreads=n:設(shè)置并發(fā)收集器年期代收集方式,使用的CPU數(shù)。并行收集線程。

六、典型配置舉例 堆大小設(shè)置

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

典型設(shè)置:

java -Xms3550m -Xmx3550m -Xmn2G -Xss128k

-Xmx3550m:設(shè)置JVM最大可用堆內(nèi)存。
-Xms3550m:設(shè)置JVM初始堆內(nèi)存為3550m。此值可以設(shè)置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內(nèi)存
-Xmn2g:設(shè)置年輕代大小為2G。整個堆大小=年輕代大小+年老代大小+持久代大小。持久代一般固定在64m,所以增大年輕代后,將會減小年老代大小。此值對系統(tǒng)性能影響較大,Sun官方推薦配置整個堆的3/8
-Xss128k:設(shè)置每個線程的堆棧大小。JDK5以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256k。具應(yīng)用的線程所需內(nèi)存大小進(jìn)行調(diào)整。在相同的物理內(nèi)存下,減小這個值能生成更多的線程。但是操作系統(tǒng)對一個進(jìn)程內(nèi)的線程數(shù)還是有限制的,不能無限生成,閥值在3000-5000左右。

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

-XX:NewRatio=4:設(shè)置年輕代(包括Eden和兩個Survivor區(qū))與年老代的比值(除去持久劉代)。設(shè)置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5。
-XX:SurvivorRatio=4:設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的大小比值。設(shè)置為4,則兩個Survivor區(qū)與一個Eden區(qū)的比值為2:4,一個Survivor?區(qū)占整個年輕代的1/6。
-XX:MaxPermSize=16m:設(shè)置持久代大小為16m。
-XX:MaxTenuringThreshold=0:設(shè)置垃圾最大年齡。如果設(shè)置為0的話,則年輕代對象不經(jīng)過Survivor區(qū),直接進(jìn)入老年代。對于年老代比較多的應(yīng)用,可以提高效率。如果將此值設(shè)為一個較大值,則年輕對象會在Survivor區(qū)進(jìn)行多次復(fù)制,這樣可以增加對象在年輕代的存活時(shí)間,增加年輕代即被回收的概論。

回收器選擇

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

吞吐量優(yōu)先的并行收集器

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

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

-XX:+UseParallelGC:選擇垃圾收集器為并行收集器。此配置僅對年輕代有效。年輕代使用并行收集,而年老代仍舊使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的線程數(shù),即:同時(shí)多少個線程一起進(jìn)行垃圾回收。此值最好配置與處理器數(shù)目相等

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

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

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

-XX:MaxGCPauseMilis=100:設(shè)置每次年輕代垃圾回收的最長時(shí)間,如果無法滿足此時(shí)間,JVM會自動調(diào)整大小,以滿足此值

java -Xmx 3550m -Xms 3550m -Xmn2g -Xss128k -XX:+UseParalleGC -XX:MaxGCPauseMilis=100 -XX:+UseAdaptiveSizePolicy

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

響應(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ā)收集。測試中配置這個以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此時(shí)年輕代大小最好用-Xmn設(shè)置。
XX:+UseParNewGC:設(shè)置年輕代為并行收集,可與CMS收集同時(shí)使用JDK5.0以上JVM會根據(jù)系統(tǒng)配置自行設(shè)置,所以無需再設(shè)置此值。

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

-XX:CMSFulGCsBeforeCompaction:由于并發(fā)收集器不對內(nèi)存空間進(jìn)行壓縮、整理,所以運(yùn)行一段時(shí)間以后會產(chǎn)生“碎片”,使得運(yùn)行效率降低。此值設(shè)置運(yùn)行多少次GC以后對內(nèi)存空間進(jìn)行壓縮、整理
-XX:CMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,但是可以消除碎片

七、調(diào)優(yōu)總結(jié) 年輕代大小選擇

響應(yīng)時(shí)間優(yōu)先的應(yīng)用:盡可能設(shè)大,直到接近系統(tǒng)的最低響應(yīng)時(shí)間限制(根據(jù)實(shí)際情況選擇);在此種情況下,年輕代收集發(fā)生的頻率也是最小的。同時(shí),減少到達(dá)年老代的對象
吞吐量優(yōu)先的應(yīng)用:盡可能的設(shè)置大,可能到達(dá)Gbit的程度。因?yàn)閷憫?yīng)時(shí)間沒有要求,垃圾收集可以并行進(jìn)行,一般適合8CPU以上的應(yīng)用。

年老代大小選擇

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

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

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

傳統(tǒng)GC信息

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

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

較小堆引起的碎片問題

因?yàn)?b>年老代的并發(fā)收集器使用標(biāo)記、清除算法,所以不會對堆進(jìn)行壓縮。當(dāng)收集器回收時(shí),他會把相鄰的空間進(jìn)行合并,這樣可以分配給較大的對象。但是,當(dāng)堆空間較小時(shí),運(yùn)行一段時(shí)間以后,就會出現(xiàn)“碎片”,如果并發(fā)收集器找不到足夠的空間,那么并發(fā)收集器將會停止,然后使用傳統(tǒng)的標(biāo)記、清除方式進(jìn)行回收。如果出現(xiàn)“碎片”,可能需要進(jìn)行如下配置:
-XX:+UseCMSCompactAtFullCollection:使用并發(fā)收集器時(shí),開啟對年老代的壓縮。
-XX:CMSFullGCsBeforeCompaction=0:上面配置開啟的情況下,這里設(shè)置多少次Full?GC后,對年老代進(jìn)行壓縮。

八、常見異常

內(nèi)存泄漏檢查
內(nèi)存泄漏一般可以理解為系統(tǒng)資源(各方面的資源,堆、棧、線程等)在錯誤使用的情況下,導(dǎo)致使用完畢的資源無法回收(或沒有回收),從而導(dǎo)致新的資源分配請求無法完成,引起系統(tǒng)錯誤。內(nèi)存泄漏對系統(tǒng)危害比較大,因?yàn)樗梢灾苯訉?dǎo)致系統(tǒng)的崩潰。需要區(qū)別一下,內(nèi)存泄漏和系統(tǒng)超負(fù)荷兩者是有區(qū)別的,雖然可能導(dǎo)致的最終果是一樣的。內(nèi)存泄漏是用完的資源沒有回收引起錯誤,而系統(tǒng)超負(fù)荷則是系統(tǒng)確實(shí)沒有那么多資源可以分配了(其他的資源都在使用)。

年老代堆空間被占滿
異常:java.lang.OutOfMemoryError.Java heap?space
說明:堆大小與使用堆成鋸齒狀。這是最典型的內(nèi)存泄漏方式,簡單說就是所有堆空間都被無法回收的垃圾對象占滿,虛擬機(jī)無法再在分配新空間。鋸齒狀每一次形成三角的頂點(diǎn)而下降為回收點(diǎn),所有谷底部分表示是一次垃圾回收后剩余的內(nèi)存。連接所有谷底的點(diǎn),可以發(fā)現(xiàn)一條由底到高的線,這說明,隨時(shí)間的推移,系統(tǒng)的堆空間被不斷占滿,最終會占滿整個堆空間。因此可以初步認(rèn)為系統(tǒng)內(nèi)部可能有內(nèi)存泄漏。(上面的圖僅供示例,在實(shí)際情況下收集數(shù)據(jù)的時(shí)間需要更長,比如幾個小時(shí)或者幾天)
解決:通過jmap把可用對象打出來,這種方式解決起來也比較容易,一般就是根據(jù)垃極回收前后情況對比,同時(shí)根據(jù)對象引用情況(常見的集合對象引用)分析,基本都可以找到泄漏點(diǎn)。

持久代被占滿
異常:java.lang.OutOfMemoryError:PermGen?space
說明:Perm空間被占滿。無法為新的cdass分配存儲空間而引發(fā)的異常。這個異常以前是沒有的,但是在Java反射大量使用的今天這個異常比較常見了。主要原因就是大量動態(tài)反射生成的類不斷被加載,最終導(dǎo)致Perm區(qū)被占滿。
更可怕的是,不同的classLoader即便使用了相同的類,但是都會對其進(jìn)行加載,相當(dāng)于同一個東西,如果有N個classLoader那么他將會被加載N次。因此,某些情況下,這個問題基本視為無解。當(dāng)然,存在大量classLoader和大量反射類的情況其實(shí)也不多。
解決:1.-XX:MaxPermSize=16m。2.換用JDK。比如JRocket。

堆棧溢出
異常:java.lang.StackOverflowError
說明:一般就是遞歸沒返回,或者循環(huán)調(diào)用造成.

線程堆棧滿
異常:Fatal:Stack size too small
說明:java中一個線程的空間大小是有限制的。JDK5.0以后這個值是1M。與這個線程相關(guān)的數(shù)據(jù)會保存在其中。但是當(dāng)線程空間滿了以后,將會出現(xiàn)上面異常。
解決:增加線程棧大小。-Xss2m。但這個配置無法解決根本問題,還要看代碼部分是否有造成泄漏的部分。

系統(tǒng)內(nèi)存被占滿
異常:java.lang.OutOfMemoryError:unable?to?create?new.native?thread
說明:
這個異常是由于操作系統(tǒng)沒有足夠的資源來產(chǎn)生這個線程造成的。系統(tǒng)創(chuàng)建線程時(shí),除了要在Java堆中分配內(nèi)存外,操作系統(tǒng)本身也需要分配資源來創(chuàng)建線程。因此,當(dāng)線程數(shù)量大到一定程度以后,堆中或許還有空間,但是操作系統(tǒng)分配不出資源來了,就出現(xiàn)這個異常了。
分配給Java虛擬機(jī)的內(nèi)存越多,系統(tǒng)剩余的資源就越少,因此,當(dāng)系統(tǒng)內(nèi)存固定時(shí),分配給Java虛擬機(jī)的內(nèi)存越多,那么,系統(tǒng)總共能夠產(chǎn)生的線程也就越少,兩者成反比的關(guān)系。同時(shí),可以通過修改-Xss來減少分配給單個線程的空間,也可以增加系統(tǒng)總共內(nèi)生產(chǎn)的線程數(shù)。
解決:1.重新設(shè)計(jì)系統(tǒng)減少線程數(shù)量。2.線程數(shù)量不能減少的情況下,通過-Xss減小單個線程大小。以便能生產(chǎn)更多的線程。

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

JVM可視化監(jiān)控工具
Jconsole:jdk自帶,功能簡單,但是可以在系統(tǒng)有一定負(fù)荷的的情況下使用。對垃圾回收算法有很詳細(xì)的跟蹤。
Jprofile:商業(yè)軟件,需要付費(fèi)。功能強(qiáng)大。
VisualVM:JDK自帶,功能強(qiáng)大,與JProfile類似。推薦。

JVM調(diào)優(yōu)監(jiān)控工具
jstat(性能分析)
https://www.jianshu.com/p/213710fb9e40

jstack(分析線程情況)
https://jingyan.baidu.com/article/4f34706e3ec075e387b56df2.html
http://www.importnew.com/23601.html

jmap(堆棧dump)
https://www.cnblogs.com/kongzhongqijing/articles/3621163.html

jinfo(jvm信息情況)
https://www.jianshu.com/p/ece32dacce64

jps(jvm進(jìn)程情況)
https://www.jianshu.com/p/d39b2e208e72

JVM調(diào)優(yōu)工具轉(zhuǎn)載至一些優(yōu)質(zhì)的博客,可以詳細(xì)學(xué)習(xí)參考

文檔整理于JVM視頻學(xué)習(xí)課程,感謝提供教育視頻的IT人才們,整理分享還未接觸到這層知識的人們,也收藏個人學(xué)習(xí)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/74161.html

相關(guān)文章

  • Java性能調(diào)優(yōu)概述

    摘要:性能調(diào)優(yōu)概述性能優(yōu)化有風(fēng)險(xiǎn)和弊端,性能調(diào)優(yōu)必須有明確的目標(biāo),不要為了調(diào)優(yōu)而調(diào)優(yōu)盲目調(diào)優(yōu),風(fēng)險(xiǎn)遠(yuǎn)大于收益程序性能的主要表現(xiàn)點(diǎn)執(zhí)行速度程序的反映是否迅速,響應(yīng)時(shí)間是否足夠短內(nèi)存分配內(nèi)存分配是否合理,是否過多地消耗內(nèi)存或者存在內(nèi)存泄漏啟動時(shí)間程序 [TOC] Java性能調(diào)優(yōu)概述 性能優(yōu)化有風(fēng)險(xiǎn)和弊端,性能調(diào)優(yōu)必須有明確的目標(biāo),不要為了調(diào)優(yōu)而調(diào)優(yōu)!!!盲目調(diào)優(yōu),風(fēng)險(xiǎn)遠(yuǎn)大于收益!!! 程序性...

    ad6623 評論0 收藏0
  • 近期Java高級工程師面試總結(jié)

    摘要:面試總結(jié)最近兩周面試了幾家公司高級工程師的職位,主要有宜信網(wǎng)信金融阿里高德口袋購物。目前有部分公司已經(jīng)面試通過,兩家在等消息。今天趁熱把常見面試內(nèi)容總結(jié)一下。可以用來完成統(tǒng)一命名服務(wù)狀態(tài)同步服務(wù)集群管理分布式應(yīng)用配置項(xiàng)等管理工作。 面試總結(jié) 最近兩周面試了幾家公司Java高級工程師的職位,主要有宜信、網(wǎng)信金融、阿里高德、口袋購物。目前有部分公司已經(jīng)面試通過,兩家在等消息。今天趁熱把常見...

    raoyi 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<