国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

jvm性能調(diào)優(yōu)(轉(zhuǎn)載)

2019-11-15 00:54:44
字體:
供稿:網(wǎng)友
jvm性能調(diào)優(yōu)(轉(zhuǎn)載)

網(wǎng)上看到一位javaeye的同志寫的文章,感覺總結(jié)的比較好,雖然也研究過這些,但沒有系統(tǒng)的總結(jié)過,覺得是好文章,先收藏了,現(xiàn)在轉(zhuǎn)載下來!

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

Java虛擬機(jī)中,數(shù)據(jù)類型可以分為兩類:基本類型引用類型 。基本類型的變量保存原始值,即:他代表的值就是數(shù)值本身;而引用類型的變量保存引用值。“引用值”代表了某個對象的引用,而不是對象本身,對象本身存放在這個引用值所表示的地址的位置。

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

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

堆與棧

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

棧是運(yùn)行時的單位,而堆是存儲的單位

棧解決程序的運(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ù)嗎 ?

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

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

第三,棧因?yàn)檫\(yùn)行時的需要,比如保存系統(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ù)據(jù) 結(jié)構(gòu),也編寫的處理數(shù)據(jù)的邏輯。不得不承認(rèn),面向?qū)ο蟮脑O(shè)計(jì),確實(shí)很美。

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

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

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

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

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

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

要說明這個問題,先要明確兩點(diǎn):

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

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

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

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

對象,從某種意義上說,是由基本類型組成的。可以把一個對象看作為一棵樹,對象的屬性如果還是對象,則還是一顆樹(即非葉子節(jié)點(diǎn)),基本類型則為樹的葉子節(jié)點(diǎn) 。程序參數(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ù)比較多時,需要適當(dāng)調(diào)大這個值,否則會出現(xiàn)java.lang.StackOverflowError異常。常見的出現(xiàn)這個異常的是無法返回的遞歸,因?yàn)榇藭r棧中保存的信息都是方法返回的記錄點(diǎn)。

Java對象的大小

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

在Java中,一個空Object對象的大小是8byte ,這個大小只是保存堆中一個沒有任何屬性的對象的大小。看下面語句:

Java代碼 收藏代碼
  1. Objectob=newObject();

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

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

Java代碼 收藏代碼
  1. ClassNewObject{
  2. intcount;
  3. booleanflag;
  4. Objectob;
  5. }
  6. 其大小為:空對象大小(8byte)+int大小(4byte)+Boolean大小(1byte)+空Object引用的大小(4byte)=17byte。但是因?yàn)镴ava在對對象內(nèi)存分配時都是以8的整數(shù)倍來分,因此大于17byte的最接近8的整數(shù)倍的是24,因此此對象的大小為24byte。

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

引用類型

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

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

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

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

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

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

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

按照基本回收策略分

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

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

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

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

復(fù)制(Copying):

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

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

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

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

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

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

按系統(tǒng)線程分

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

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

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

  • 查看圖片附件
分享到: ruby小知識:從命令行讀取文本 | jsp 調(diào)用windows的打印功能
  • 2009-11-20 10:16
  • 瀏覽 1658
  • 評論(1)
  • 收藏
  • 分類:編程語言
  • 相關(guān)推薦
評論 1 樓 vaneng 2010-07-14 引用 Java代碼 收藏代碼
  1. ClassNewObject{
  2. intcount;
  3. booleanflag;
  4. Objectob;
  5. }
類里面的基本類型沒有內(nèi)存對齊么?就像結(jié)構(gòu)體那樣.
發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 梁河县| 东乡| 关岭| 托里县| 襄垣县| 维西| 万源市| 宁阳县| 漳州市| 永吉县| 静海县| 庆云县| 博野县| 陇川县| 台北市| 南平市| 昆山市| 沂源县| 昆明市| 霍州市| 南宁市| 贺州市| 凭祥市| 西丰县| 赞皇县| 洛浦县| 乐都县| 汉寿县| 锡林浩特市| 龙山县| 额尔古纳市| 深圳市| 普洱| 武川县| 全椒县| 高州市| 抚远县| 上饶市| 辽源市| 井研县| 连城县|