基于Java的應(yīng)用最大的問題莫過于出現(xiàn)Out Of Memory Error(內(nèi)存溢出錯(cuò)誤),通常出現(xiàn)OOME問題的應(yīng)用都會(huì)有以下一些表現(xiàn):
l Jvm crash
l 性能奇差
l Jvm似乎在不斷的進(jìn)行垃圾回收收集,這通常致使程序停止運(yùn)行甚至服務(wù)崩潰
而且一旦出現(xiàn)這種情況,一般都需要重新啟動(dòng)應(yīng)用服務(wù)器。
OutOfMemoryError
如果JVM里運(yùn)行的程序, 它的heap space和perm gen都滿了,這個(gè)時(shí)候程序還企圖創(chuàng)建新的對象實(shí)例的話,jvm gc就會(huì)啟動(dòng),試圖釋放足夠的內(nèi)存來創(chuàng)建這個(gè)對象。這個(gè)時(shí)候如果gc無法釋放出足夠的內(nèi)存,它就會(huì)拋出OutOfMemoryError內(nèi)存溢出錯(cuò)誤。
OutOfMemoryError通常是java內(nèi)存泄漏引起的。內(nèi)存泄漏的原因是一個(gè)對象雖然不被使用了,但是依然還有對象引用它,因此jvm gc就不會(huì)釋放它所占據(jù)的內(nèi)存,堆中也就少了塊可用的空間。
通常解決這類問題需要從以下兩個(gè)方面著手:
l 分析內(nèi)存數(shù)據(jù)
l 觀察堆的增長方式
SUN的jvm內(nèi)存池被劃分為以下幾個(gè)部分:
Eden Space (heap)
內(nèi)存最初從這個(gè)線程池分配給大部分對象。
Survivor Space (heap)
用于保存在eden space內(nèi)存池中經(jīng)過垃圾回收后沒有被回收的對象。
Tenured Generation (heap)
用于保持已經(jīng)在survivor space內(nèi)存池中存在了一段時(shí)間的對象。
Permanent Generation (non-heap)
保存虛擬機(jī)自己的靜態(tài)(reflective)數(shù)據(jù),例如類(class)和方法(method)對象。Java虛擬機(jī)共享這些類數(shù)據(jù)。這個(gè)區(qū)域被分割為只讀的和只寫的。
Code Cache (non-heap)
HotSpot Java虛擬機(jī)包括一個(gè)用于編譯和保存本地代碼(native code)的內(nèi)存,叫做“代碼緩存區(qū)”(code cache)。
簡單來講,jvm的內(nèi)存回收過程是這樣的:
對象在Eden Space創(chuàng)建,當(dāng)Eden Space滿了的時(shí)候,gc就把所有在Eden Space中的對象掃描一次,把所有有效的對象復(fù)制到第一個(gè)Survivor Space,同時(shí)把無效的對象所占用的空間釋放。當(dāng)Eden Space再次變滿了的時(shí)候,就啟動(dòng)移動(dòng)程序把Eden Space中有效的對象復(fù)制到第二個(gè)Survivor Space,同時(shí),也將第一個(gè)Survivor Space中的有效對象復(fù)制到第二個(gè)Survivor Space。如果填充到第二個(gè)Survivor Space中的有效對象被第一個(gè)Survivor Space或Eden Space中的對象引用,那么這些對象就是長期存在的,此時(shí)這些對象將被復(fù)制到Permanent Generation。
若垃圾收集器依據(jù)這種小幅度的調(diào)整收集不能騰出足夠的空間,就會(huì)運(yùn)行Full GC,此時(shí)jvm gc停止所有在堆中運(yùn)行的線程并執(zhí)行清除動(dòng)作。
下面是整理的一份JVM調(diào)優(yōu)工具介紹,重點(diǎn)介紹幾個(gè)關(guān)鍵的:
jps
http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jps.html
jps使用很簡單,在console輸入jps即可,它是用來查看JVM里面所有進(jìn)程的具體狀態(tài), 包括進(jìn)程ID,進(jìn)程啟動(dòng)的路徑等等。
jconsole
http://java.sun.com/j2se/1.5.0/docs/guide/management/jconsole.html
jconsole是基于Java Management Extensions (JMX)的實(shí)時(shí)圖形化監(jiān)測工具,它利用了內(nèi)建到JVM里面的JMX指令來提供實(shí)時(shí)的性能和資源的監(jiān)控,包括了Java程序的內(nèi)存使用,Heap size, 線程的狀態(tài),類的分配狀態(tài)和空間使用等等。
注意:JBoss 4.0.2及以下版本不支持jconsole,在國際站測試過程中遇到了這個(gè)問題,查了JBoss JIRA才知道4.0.2版本的JMX實(shí)現(xiàn)并不標(biāo)準(zhǔn)。。。
jstat
http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jstat.html
jstat利用了JVM內(nèi)建的指令對Java應(yīng)用程序的資源和性能進(jìn)行實(shí)時(shí)的命令行的監(jiān)控,包括了對Heap size和垃圾回收狀況的監(jiān)控等等。它包含了以下幾個(gè)options,能夠?qū)c情況進(jìn)行詳細(xì)的監(jiān)控:
-class
-compiler
-gc
-gccapacity
-gccause
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcpermcapacity
-gcutil
-printcompilation
我們常用的命令如:
jstat -gcutil -t -h10 $jvmpid 1s > _jvmstat_$jvmpid.$now.log &
它表示監(jiān)控jvm gc狀態(tài),每秒鐘刷新一次。每10次顯示一次表頭。
這是一個(gè)非常實(shí)用的工具,對我們jvm的gc狀態(tài)和監(jiān)控非常有幫助。對于希望遠(yuǎn)程監(jiān)控jvm狀態(tài)的人,則jstatd比較有用了。
jstatd
http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jstatd.html
jstatd是jstat的守護(hù)進(jìn)程,它能夠提供遠(yuǎn)程jstat功能。使用它的服務(wù)需要設(shè)置security policy,一種方式是修改${java.home}/jre/lib/security/java.policy文件,在最后加入:
grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
又或者,定義jstatd.policy文件,加入上面的策略,然后用如下腳本啟動(dòng)jstatd:
jstatd -J-Djava.security.policy=jstatd.policy
遠(yuǎn)程的監(jiān)控命令:
jstat pid@hostip interval
如:
jstat 1234@127.0.0.1 1000
此時(shí)會(huì)監(jiān)控127.0.0.1機(jī)器上進(jìn)程id為1234的jvm gc情況,間隔1秒。
還有幾個(gè):
jstack
http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jstack.html
jstack工具可以用來獲得java程序崩潰生成的core文件的java stack和native stack的信息,從而可以輕松地知道java程序是如何崩潰和在程序何處發(fā)生問題。另外,jstack工具還可以附屬到正在運(yùn)行的java程序中,看到當(dāng)時(shí)運(yùn)行的java程序的java stack和native stack的信息,如果現(xiàn)在運(yùn)行的java程序呈現(xiàn)掛起的情況,jstack是非常有用的。
目前只有在Solaris和Linux的JDK版本里面才有。
jinfo
http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jinfo.html
jinfo可以從core文件里面知道崩潰的Java應(yīng)用程序的配置信息。目前只有在Solaris和Linux的JDK版本里面才有。
jmap
http://java.sun.com/j2se/1.5.0/docs/tooldocs/share/jmap.html
jmap可以從core文件或進(jìn)程中獲得內(nèi)存的具體匹配情況,包括Heap size, Perm size等等。目前只有在Solaris和Linux的JDK版本里面才有。
JVM提供了很多調(diào)優(yōu)參數(shù),在此次調(diào)優(yōu)過程中,我們發(fā)現(xiàn)采用以下幾個(gè)jvm參數(shù)會(huì)使jvm變得非常穩(wěn)定,這也是sun官方提供的生產(chǎn)環(huán)境調(diào)優(yōu)參數(shù),在sun的很多產(chǎn)品調(diào)優(yōu)腳本中都可以看到這幾個(gè)參數(shù)設(shè)置。
標(biāo)準(zhǔn)的JVM GC回收器不會(huì)回收Permanent Generation,但JVM提供了并發(fā)GC回收器來實(shí)現(xiàn),首先我們要指定回收機(jī)制:
-XX:+UseConcMarkSweepGC
該參數(shù)適用于多核并存在緩存機(jī)制的應(yīng)用中。它生成的gc回收曲線變化相比較并行GC而言更加平滑穩(wěn)定。詳細(xì)的參數(shù)說明可以參考sun的官方文檔。
上面的參數(shù)是第一步,下一個(gè)參數(shù)告訴GC回收器能夠操作Permanent Generation:
-XX:+CMSPermGenSweepingEnabled
而GC回收器是不會(huì)操作Classes的,這時(shí)候我們可以指定以下參數(shù)可以使得jvm能夠unload class:
-XX:+CMSClassUnloadingEnabled
聯(lián)系客服