淺談JVM
JVM(Java Virtual Machine):Java虛擬機(jī),所有的Java程序都在Java虛擬機(jī)中運(yùn)行。
元數(shù)據(jù):在本文中指用于描述類和接口定義的數(shù)據(jù)。
在我做J2EE系統(tǒng)開(kāi)發(fā)的工作生涯中,經(jīng)常遇到技術(shù)人員或客戶發(fā)出諸如此類的感慨:我的J2EE應(yīng)用系統(tǒng)處理的數(shù)據(jù)量不大,系統(tǒng)體積也不大,技術(shù)架構(gòu)也沒(méi)有問(wèn)題,我的應(yīng)用服務(wù)器的內(nèi)存有4G或8G;系統(tǒng)運(yùn)行起來(lái)很慢,還經(jīng)常出現(xiàn)內(nèi)存溢出錯(cuò)誤。真是無(wú)奈!每次遇到這樣的情況,我心中都會(huì)忍不住竊笑之。
其實(shí)他們所遇到這種情況,不是技術(shù)架構(gòu)上的問(wèn)題,不是系統(tǒng)本身的問(wèn)題,也不是應(yīng)用服務(wù)器的問(wèn)題,也可能不是服務(wù)器的內(nèi)存資源真的不足的問(wèn)題。他們花了很多時(shí)間在J2EE應(yīng)用系統(tǒng)本身上找問(wèn)題(當(dāng)然一般情況下,這種做法是對(duì)的;當(dāng)出現(xiàn)問(wèn)題時(shí),在自身上多找找有什么不足),結(jié)果還是解決不了問(wèn)題。他們卻忽略了很重要的一點(diǎn):J2EE應(yīng)用系統(tǒng)是運(yùn)行在J2EE應(yīng)用服務(wù)器上的,而J2EE應(yīng)用服務(wù)器又是運(yùn)行在JVM(Java Virtual Machine)上的。
其實(shí)在生產(chǎn)環(huán)境中JVM參數(shù)的優(yōu)化和設(shè)置對(duì)J2EE應(yīng)用系統(tǒng)性能有著決定性的作用。本篇我們就來(lái)分析JAVA的創(chuàng)建者SUN 公司的JVM的內(nèi)存管理機(jī)制(在現(xiàn)實(shí)中絕大多數(shù)的應(yīng)用服務(wù)器是運(yùn)行在SUN公司的JVM上的,當(dāng)然除了SUN公司的JVM,還有IBM的JVM,Bea的JVM等);下篇咱們具體講解怎樣優(yōu)化JVM的參數(shù)以達(dá)到優(yōu)化J2EE應(yīng)用的目的。
咱們先來(lái)看JVM的內(nèi)存管理制吧,JVM的早期版本并沒(méi)有進(jìn)行分區(qū)管理;這樣的后果是JVM進(jìn)行垃圾回收時(shí),不得不掃描JVM所管理的整片內(nèi)存,所以搜集垃圾是很耗費(fèi)資源的事情,也是早期JAVA程序的性能低下的主要原因。隨著JVM的發(fā)展,JVM引進(jìn)了分區(qū)管理的機(jī)制。
采用分區(qū)管理機(jī)制的JVM將JVM所管理的所有內(nèi)存資源分為2個(gè)大的部分。***存儲(chǔ)區(qū)(Permanent Space)和堆空間(The Heap Space)。其中堆空間又分為新生區(qū)(Young (New) generation space)和養(yǎng)老區(qū)(Tenure (Old) generation space),新生區(qū)又分為伊甸園(Eden space),幸存者0區(qū)(Survivor 0 space)和幸存者1區(qū)(Survivor 1 space)。具體分區(qū)如下圖:
那JVM他的這些分區(qū)各有什么用途,請(qǐng)看下面的解說(shuō)。
***存儲(chǔ)區(qū)(Permanent Space):***存儲(chǔ)區(qū)是JVM的駐留內(nèi)存,用于存放JDK自身所攜帶的Class,Interface的元數(shù)據(jù),應(yīng)用服務(wù)器允許必須的Class,Interface的元數(shù)據(jù)和Java程序運(yùn)行時(shí)需要的Class和Interface的元數(shù)據(jù)。被裝載進(jìn)此區(qū)域的數(shù)據(jù)是不會(huì)被垃圾回收器回收掉的,關(guān)閉JVM時(shí),釋放此區(qū)域所控制的內(nèi)存。
堆空間(The Heap Space):是JAVA對(duì)象生死存亡的地區(qū),JAVA對(duì)象的出生,成長(zhǎng),死亡都在這個(gè)區(qū)域完成。堆空間又分別按JAVA對(duì)象的創(chuàng)建和年齡特征分為養(yǎng)老區(qū)和新生區(qū)。
新生區(qū)(Young (New) generation space):新生區(qū)的作用包括JAVA對(duì)象的創(chuàng)建和從JAVA對(duì)象中篩選出能進(jìn)入養(yǎng)老區(qū)的JAVA對(duì)象。
伊甸園(Eden space):JAVA對(duì)空間中的所有對(duì)象在此出生,該區(qū)的名字因此而得名。也即是說(shuō)當(dāng)你的JAVA程序運(yùn)行時(shí),需要?jiǎng)?chuàng)建新的對(duì)象,JVM將在該區(qū)為你創(chuàng)建一個(gè)指定的對(duì)象供程序使用。創(chuàng)建對(duì)象的依據(jù)即是***存儲(chǔ)區(qū)中的元數(shù)據(jù)。
幸存者0區(qū)(Survivor 0 space)和幸存者1區(qū)(Survivor1 space):當(dāng)伊甸園的控件用完時(shí),程序又需要?jiǎng)?chuàng)建對(duì)象;此時(shí)JVM的垃圾回收器將對(duì)伊甸園區(qū)進(jìn)行垃圾回收,將伊甸園區(qū)中的不再被其他對(duì)象所引用的對(duì)象進(jìn)行銷毀工作。同時(shí)將伊甸園中的還有其他對(duì)象引用的對(duì)象移動(dòng)到幸存者0區(qū)。幸存者0區(qū)就是用于存放伊甸園垃圾回收時(shí)所幸存下來(lái)的JAVA對(duì)象。
當(dāng)將伊甸園中的還有其他對(duì)象引用的對(duì)象移動(dòng)到幸存者0區(qū)時(shí),如果幸存者0區(qū)也沒(méi)有空間來(lái)存放這些對(duì)象時(shí),JVM的垃圾回收器將對(duì)幸存者0區(qū)進(jìn)行垃圾回收處理,將幸存者0區(qū)中不在有其他對(duì)象引用的JAVA對(duì)象進(jìn)行銷毀,將幸存者0區(qū)中還有其他對(duì)象引用的對(duì)象移動(dòng)到幸存者1區(qū)。幸存者1區(qū)的作用就是用于存放幸存者0區(qū)垃圾回收處理所幸存下來(lái)的JAVA對(duì)象。
養(yǎng)老區(qū)(Tenure (Old) generation space):用于保存從新生區(qū)篩選出來(lái)的JAVA對(duì)象。
上面我們看了JVM的內(nèi)存分區(qū)管理,現(xiàn)在我們來(lái)看JVM的垃圾回收工作是怎樣運(yùn)作的。首先當(dāng)啟動(dòng)J2EE應(yīng)用服務(wù)器時(shí),JVM隨之啟動(dòng),并將JDK的類和接口,應(yīng)用服務(wù)器運(yùn)行時(shí)需要的類和接口以及J2EE應(yīng)用的類和接口定義文件也及編譯后的Class文件或JAR包中的Class文件裝載到JVM的***存儲(chǔ)區(qū)。在伊甸園中創(chuàng)建JVM,應(yīng)用服務(wù)器運(yùn)行時(shí)必須的JAVA對(duì)象,創(chuàng)建J2EE應(yīng)用啟動(dòng)時(shí)必須創(chuàng)建的JAVA對(duì)象;J2EE應(yīng)用啟動(dòng)完畢,可對(duì)外提供服務(wù)。
JVM在伊甸園區(qū)根據(jù)用戶的每次請(qǐng)求創(chuàng)建相應(yīng)的JAVA對(duì)象,當(dāng)伊甸園的空間不足以用來(lái)創(chuàng)建新JAVA對(duì)象的時(shí)候,JVM的垃圾回收器執(zhí)行對(duì)伊甸園區(qū)的垃圾回收工作,銷毀那些不再被其他對(duì)象引用的JAVA對(duì)象(如果該對(duì)象僅僅被一個(gè)沒(méi)有其他對(duì)象引用的對(duì)象引用的話,此對(duì)象也被歸為沒(méi)有存在的必要,依此類推),并將那些被其他對(duì)象所引用的JAVA對(duì)象移動(dòng)到幸存者0區(qū)。
如果幸存者0區(qū)有足夠控件存放則直接放到幸存者0區(qū);如果幸存者0區(qū)沒(méi)有足夠空間存放,則JVM的垃圾回收器執(zhí)行對(duì)幸存者0區(qū)的垃圾回收工作,銷毀那些不再被其他對(duì)象引用的JAVA對(duì)象(如果該對(duì)象僅僅被一個(gè)沒(méi)有其他對(duì)象引用的對(duì)象引用的話,此對(duì)象也被歸為沒(méi)有存在的必要,依此類推),并將那些被其他對(duì)象所引用的JAVA對(duì)象移動(dòng)到幸存者1區(qū)。
如果幸存者1區(qū)有足夠控件存放則直接放到幸存者1區(qū);如果幸存者0區(qū)沒(méi)有足夠空間存放,則JVM的垃圾回收器執(zhí)行對(duì)幸存者0區(qū)的垃圾回收工作,銷毀那些不再被其他對(duì)象引用的JAVA對(duì)象(如果該對(duì)象僅僅被一個(gè)沒(méi)有其他對(duì)象引用的對(duì)象引用的話,此對(duì)象也被歸為沒(méi)有存在的必要,依此類推),并將那些被其他對(duì)象所引用的JAVA對(duì)象移動(dòng)到養(yǎng)老區(qū)。
如果養(yǎng)老區(qū)有足夠控件存放則直接放到養(yǎng)老區(qū);如果養(yǎng)老區(qū)沒(méi)有足夠空間存放,則JVM的垃圾回收器執(zhí)行對(duì)養(yǎng)老區(qū)區(qū)的垃圾回收工作,銷毀那些不再被其他對(duì)象引用的JAVA對(duì)象(如果該對(duì)象僅僅被一個(gè)沒(méi)有其他對(duì)象引用的對(duì)象引用的話,此對(duì)象也被歸為沒(méi)有存在的必要,依此類推),并保留那些被其他對(duì)象所引用的JAVA對(duì)象。
如果到***養(yǎng)老區(qū),幸存者1區(qū),幸存者0區(qū)和伊甸園區(qū)都沒(méi)有空間的話,則JVM會(huì)報(bào)告“JVM堆空間溢出(java.lang.OutOfMemoryError: Java heap space)”,也即是在堆空間沒(méi)有空間來(lái)創(chuàng)建對(duì)象。
這就是JVM的內(nèi)存分區(qū)管理,相比不分區(qū)來(lái)說(shuō);一般情況下,垃圾回收的速度要快很多;因?yàn)樵跊](méi)有必要的時(shí)候不用掃描整片內(nèi)存而節(jié)省了大量時(shí)間。
通常大家還會(huì)遇到另外一種內(nèi)存溢出錯(cuò)誤“***存儲(chǔ)區(qū)溢出(java.lang.OutOfMemoryError: Java Permanent Space)”。
原文地址:http://blog.csdn.net/soulx/archive/2010/03/01/5335203.aspx
【編輯推薦】