b]問題陳述: [/b]
tomcat服務(wù)器運(yùn)行一段時間后,tomcat控制臺報錯: java.lang.outOfMemoryError,java.lang.outOfMemoryError 然后tomcat服務(wù)立即死,只有重啟tomcat才能恢復(fù)服務(wù) [b]初步確定原因[/b] 1,java虛擬機(jī)jvm 2,程序不嚴(yán)密,產(chǎn)生太多的垃圾 [b]解決對策:[/b] 針對原因1: 增加jvm的內(nèi)存大小。 JVM即是java虛擬機(jī),它運(yùn)行時候占用一定的內(nèi)存,其大小是有限定的,如果程序在運(yùn)行時jvm占用的內(nèi)存大于某個限度,則會產(chǎn)生內(nèi)存益處,也就是“java.lang.outofmemoryerror”。 作為web容器的tomcat(包括resin等)在運(yùn)行時候,會直接或間接產(chǎn)生一個java.exe進(jìn)程,可以看成一個jvm進(jìn)程。由于這些web容器在不停的運(yùn)行,也就是jvm不停的工作,jvm會不斷的產(chǎn)生垃圾(所胃的垃圾,簡單可認(rèn)為是指程序進(jìn)行后遺留下的無用的對象),也會不停的 針對此,我們可以設(shè)想,如果jvm內(nèi)存的沒有限度,并且有無限大的內(nèi)存,那jvm就永遠(yuǎn)不會出現(xiàn)內(nèi)存溢出了。但是,傻子都知道我們在做白日夢。既然這樣行不通,那我們就退一步,所胃退一步海闊天空嘛,我們可以適當(dāng)增大jvm的最大內(nèi)存,以緩解jvm來不及回收垃圾而導(dǎo)致的內(nèi)存不斷增長。jdk1.4默認(rèn)jvm的最大內(nèi)存為128M,我們可以把tomcat等web容器的jvm最大內(nèi)存加大,比如我們可以增大到256M,甚至1G等,只要不超過計算的內(nèi)存都行,不過凡事都有個度,設(shè)置過大肯定不是最好的。至于要個jvm設(shè)置多大的內(nèi)存則要通過我們不斷嘗試。 如何增加jvm的內(nèi)存呢? 第一,在執(zhí)行某個class文件時候,可以使用java -Xmx256M aa.class來設(shè)置運(yùn)行aa.class時jvm所允許占用的最大內(nèi)存為256M。 第二,對tomcat容器,可以在啟動時對jvm設(shè)置內(nèi)存限度。對tomcat,可以在catalina.bat中添加: "set CATALINA_OPTS=-Xms128M -Xmx256M set JAVA_OPTS=-Xms128M -Xmx256M",或者把%CATALINA_OPTS%和%JAVA_OPTS%代替為-Xms128M -Xmx256M,其具體操作可以到網(wǎng)上去找找。 第三,對resin容器,同樣可以在啟動時對jvm設(shè)置內(nèi)存限度。在bin @echo off call "httpd.exe" "-Xms128M" "-Xmx256M" :end 其中"-Xms128M"為最小內(nèi)存,"-Xmx256M"為最大內(nèi)存。 第四,其他容器,如ibm webswhere,可以通過 第五,修改jdk內(nèi)核,個人想法,但沒去研究過。
針對原因2: 由于jvm產(chǎn)生的垃圾是由我們所寫的代碼產(chǎn)生的,質(zhì)量好的代碼產(chǎn)生的垃圾少,相反就會產(chǎn)生很多垃圾。由于jvm的最大內(nèi)存不能無限增大,故增大jvm的最大內(nèi)存應(yīng)該是在代碼已經(jīng)達(dá)到很優(yōu)化時才實施的,所以優(yōu)化程序才是我們最先要做的。 如何優(yōu)化程序: 第一,避免死循環(huán)。仔細(xì)檢查程序,防止出現(xiàn)死循環(huán),這是比較容易檢查的。 第二,可以適當(dāng)手動回收垃圾 第三, 應(yīng)該及時釋放種資源:內(nèi)存, 釋放資源的時候不能依賴于java的垃圾自動回收機(jī)制,最好也不要用finalize方法,因為無用單元回收不是一個完全可以確定的過程,作為低優(yōu)先級進(jìn)程,往往是系統(tǒng)沒有內(nèi)存時才調(diào)用垃圾回收進(jìn)程。 由于我們在Java程序中聲明了好多對象,占用了內(nèi)存空間,程序結(jié)束時沒有將這些對象或?qū)ο蟮囊眠M(jìn)行釋放,從而導(dǎo)致Java虛擬機(jī)(JVM)進(jìn)行垃圾回收(GC)時,不能夠回收這些對象。這樣,Java所用的內(nèi)存就會一直增加,直至溢出,進(jìn)而導(dǎo)致Resin死機(jī)。 導(dǎo)致Java內(nèi)存溢出的根本原因是Java程序的不規(guī)范或不健壯。因此,從根本上解決Java內(nèi)存溢出的唯一方法就是修改Java程序,及時地釋放沒用的對象,釋放內(nèi)存空間。 除了這個方法以外,還有一些應(yīng)急措施,可以臨時緩解一下系統(tǒng)的運(yùn)行。Resin默認(rèn)情況是死機(jī)以后不能訪問網(wǎng)站,必須手動重啟Resin,但不可能一直看在機(jī)器旁邊,看Resin有沒有死機(jī)。所以這里介紹一種讓Resin自重啟的方法。 打開Resin的配置文件resin.conf(一般情況下,該文件在Resin目錄的conf文件夾下)。里面有一段內(nèi)容如下所示: 這段內(nèi)容默認(rèn)情況下是被注釋的。它的功能是讓Resin每隔一分鐘就測試一下能否訪問/ping/ping.jsp文件,測試時間是1s,如果不成功,就重試三次。如果三次都不成功,Resin就自動重啟。所以將這段話下半部分(從"")的注釋去掉,然后在Resin的發(fā)布目錄中新建一個文件夾ping,在ping文件夾下新建一個ping.jsp文件,文件中可以寫入簡單的一句話,如:。 好,大功告成,啟動Resin,這樣就不用擔(dān)心Java內(nèi)存溢出導(dǎo)致Resin死機(jī)了,因為Resin死機(jī)后會馬上重新啟動。 當(dāng)然,這只是應(yīng)急措施,不是長久之計。如果從長計議,還是要耐心的更改Java程序?。?! 有人說: “不斷的將被選中的字符串加到某一字符串末尾,當(dāng)長度超過一定量是就提示: java.lang.StringIndexOutOfBoundsException: String index out of range: 10 ”說明String有長度限制。 看一下Java API就會知道 java.lang.StringIndexOutOfBoundsException出現(xiàn)的情況是 Thrown by String methods to indicate that an index is either negative or greater than the size of the string. For some methods such as the charAt method。 上面的錯誤是因為 String.length()<10; 而你又要取index>=10的字符,自然就會拋出上面的例外。 String其實是沒有限制的,而是當(dāng)String太大了,超過JVM的自身的內(nèi)存后會拋出 java.lang.OutOfMemoryError錯誤 下面作個實驗: public class testString{ public static void main(String args[]) { String s="abbbbb"; System.out.println("JVM MAX MEMORY: "+Runtime.getRuntime().maxMemory()/1024/1024+"M"); System.out.println("JVM IS USING MEMORY:"+Runtime.getRuntime().totalMemory()/1024/1024+"M"); Runtime.getRuntime().traceMethodCalls(true); while(true) { try{ s=s+s; }catch(Exception e) { System.out.println(e); } catch(Error o) { String unit = null; int sizeb = s.length(); int size = sizeb; int time = 0; while(size>1024) { size = size/1024; time++; } switch(time) { case 0: unit = "byte";break; case 1: unit = "k"; break; case 2: unit = "M"; break; default : unit = "byte"; } System.out.println("String has used memory:"+size+unit); System.out.println("JVM IS USING MEMORY:"+(float)Runtime.getRuntime().totalMemory()/1024/1024+"M"); System.out.println("MemoryError:"+o); break; } } } } 然后我們用JVM的默認(rèn)參數(shù)執(zhí)行(我的機(jī)器內(nèi)存是128M) java testString 結(jié)果: JVM MAX MEMORY: 128M JVM IS USING MEMORY:1M String has used memory:12M JVM IS USING MEMORY:63.5625M MemoryError:java.lang.OutOfMemoryError 開始JVM使用的內(nèi)存是1M,當(dāng)String為12M,JVM使用了63M多時 JVM溢出。 然后,我們用限制JVM內(nèi)存大小的參數(shù)來執(zhí)行,限制最大內(nèi)存5M java -mx5m testString 結(jié)果: JVM MAX MEMORY: 70M JVM IS USING MEMORY:1M String has used memory:768.0k JVM IS USING MEMORY:5.9375M MemoryError:java.lang.OutOfMemoryError 開始JVM使用的內(nèi)存是1M,當(dāng)String為768k,JVM使用了5M多時 JVM溢出。 大家還可以改變 -mx參數(shù),來進(jìn)一步做實驗。 以上兩個實驗證明,String是沒有長度限制的,而是有JVM的內(nèi)存限制了String的長度。同時說明,并不會拋出任何Exception而只會拋出Error. OutMemoryError表明程序的設(shè)計很差,或者遇到了超出編程人員所預(yù)想的大批量的數(shù)據(jù)。不管哪種情況,都只有下面這幾種解決辦法。它們是: 設(shè)計人員重新設(shè)計程序,不致使程序一次載入所有的數(shù)據(jù)。 數(shù)據(jù)可以分割成更小的塊。 可以為程序分配更多的內(nèi)存。 為Java虛擬機(jī)提供更多的內(nèi)存。 而上面的例子是為虛擬機(jī)提供更多的內(nèi)存 ======================================= 其實應(yīng)該少用String這東西,特別是 String的 +=操作 不僅原來的String對象不能繼續(xù)使用,主要是又要new出N多的新對象出來,再多的memory也要out~~ String用char array實現(xiàn),就肯定由長度限制的,不能用memory來衡量 ================================== 例如上面的程序改用StringBuffer實現(xiàn),就可以得到極大的改善。 下面是我改用StringBuffer做的測試: 注意:程序循環(huán)了2097150次! 是使用String的程序的99864倍! public class TestStringBuffer{ public static void main(String args[]) { String s="abbbbb"; StringBuffer sb = new StringBuffer(s); System.out.println("JVM IS USING MEMORY:"+ (Runtime.getRuntime().totalMemory()/1024/1024)+ "M"); Runtime.getRuntime().traceMethodCalls(true); int count = 0; while(true) { try{ sb.append(s); count++; }catch(Exception e) { System.out.println(e); } catch(Error o) { String unit = null; int size = sb.length(); size *= 2; int time = 0; while(size>1024) { size = size/1024; time++; } switch(time) { case 0: unit = "byte";break; case 1: unit = "k"; break; case 2: unit = "M"; break; default : unit = "byte"; } System.out.println("Loop times:"+count); System.out.println("String has used memory:"+size+unit); System.out.println("JVM IS USING MEMORY:"+ (float)Runtime.getRuntime().totalMemory()/1024/1024+ "M"); System.out.println("MemoryError:"+o); break; } } } } 輸出結(jié)果: JVM IS USING MEMORY:1M Loop times:2097150 String has used memory:23M JVM IS USING MEMORY:63.75M MemoryError:java.lang.OutOfMemoryError ===================== 從另一方面說,如果你要處理的字符串達(dá)到百兆甚至上GB,使用String對象,根本沒法工作,所以這個問題不需要太多討論??匆幌耲dk的源文件,String的長度是String對象的一個成員count,類型是int,不是long,也不是char。知道這些,我認(rèn)為夠了。 如何設(shè)置Tomcat的JVM虛擬機(jī)內(nèi)存大小 Tomcat本身不能直接在計算機(jī)上運(yùn)行,需要依賴于硬件基礎(chǔ)之上的操作系統(tǒng)和一個java虛擬機(jī)。您可以選擇自己的需要選擇不同的操作系統(tǒng)和對應(yīng)的JDK的版本(只要是符合Sun發(fā)布的Java規(guī)范的),但我們推薦您使用Sun公司發(fā)布的JDK。確保您所使用的版本是最新的,因為Sun公司和其它一些公司一直在為提高性能而對java虛擬機(jī)做一些升級改進(jìn)。一些報告顯示JDK1.4在性能上比JDK1.3提高了將近10%到20%。 可以給Java虛擬機(jī)設(shè)置使用的內(nèi)存,但是如果 參數(shù) 描述 -Xms JVM初始化堆的大小 -Xmx JVM堆的最大值 這兩個值的大小一般根據(jù)需要進(jìn)行設(shè)置。初始化堆的大小執(zhí)行了虛擬機(jī)在啟動時向系統(tǒng)申請的內(nèi)存的大小。一般而言,這個參數(shù)不重要。但是有的應(yīng)用程序在大負(fù)載的情況下會急劇地占用更多的內(nèi)存,此時這個參數(shù)就是顯得非常重要,如果虛擬機(jī)啟動時設(shè)置使用的內(nèi)存比較小而在這種情況下有許多對象進(jìn)行初始化,虛擬機(jī)就必須重復(fù)地增加內(nèi)存來滿足使用。由于這種原因,我們一般把-Xms和-Xmx設(shè)為一樣大,而堆的最大值受限于系統(tǒng)使用的物理內(nèi)存。一般使用數(shù)據(jù)量較大的應(yīng)用程序會使用持久對象,內(nèi)存使用有可能迅速地增長。當(dāng)應(yīng)用程序需要的內(nèi)存超出堆的最大值時虛擬機(jī)就會提示內(nèi)存溢出,并且導(dǎo)致應(yīng)用服務(wù)崩潰。因此一般建議堆的最大值設(shè)置為可用內(nèi)存的最大值的80%。 Tomcat默認(rèn)可以使用的內(nèi)存為128MB,在較大型的應(yīng)用項目中,這點(diǎn)內(nèi)存是不夠的,需要調(diào)大。 Windows下,在文件/bin/catalina.bat,Unix下,在文件/bin/catalina.sh的前面,增加如下設(shè)置: JAVA_OPTS='-Xms【初始化內(nèi)存大小】 -Xmx【可以使用的最大內(nèi)存】' 需要把這個兩個參數(shù)值調(diào)大。例如: JAVA_OPTS='-Xms256m -Xmx512m' 表示初始化內(nèi)存為256MB,可以使用的最大內(nèi)存為512MB。 另外需要考慮的是Java提供的垃圾回收機(jī)制。虛擬機(jī)的堆大小決定了虛擬機(jī)花費(fèi)在收集垃圾上的時間和頻度。收集垃圾可以接受的速度與應(yīng)用有關(guān),應(yīng)該通過分析實際的垃圾收集的時間和頻率來調(diào)整。如果堆的大小很大,那么完全垃圾收集就會很慢,但是頻度會降低。如果你把堆的大小和內(nèi)存的需要一致,完全收集就很快,但是會更加頻繁。調(diào)整堆大小的的目的是最小化垃圾收集的時間,以在特定的時間內(nèi)最大化處理客戶的請求。在基準(zhǔn)測試的時候,為保證最好的性能,要把堆的大小設(shè)大,保證垃圾收集不在整個基準(zhǔn)測試的過程中出現(xiàn)。 如果系統(tǒng)花費(fèi)很多的時間收集垃圾,請減小堆大小。一次完全的垃圾收集應(yīng)該不超過 3-5 秒。如果垃圾收集成為瓶頸,那么需要指定代的大小,檢查垃圾收集的詳細(xì)輸出,研究 垃圾收集參數(shù)對性能的影響。一般說來,你應(yīng)該使用物理內(nèi)存的 80% 作為堆大小。當(dāng)增加處理器時,記得增加內(nèi)存,因為分配可以并行進(jìn)行,而垃圾收集不是并行的。 Tomcat 5常用優(yōu)化和配置 1、JDK內(nèi)存優(yōu)化: Tomcat默認(rèn)可以使用的內(nèi)存為128MB,Windows下,在文件{tomcat_home}/bin/catalina.bat,Unix下,在文件{tomcat_home}/bin/catalina.sh的前面,增加如下設(shè)置: JAVA_OPTS='-Xms[初始化內(nèi)存大小] -Xmx[可以使用的最大內(nèi)存] 一般說來,你應(yīng)該使用物理內(nèi)存的 80% 作為堆大小。 2、連接器優(yōu)化: 在tomcat配置文件server.xml中的配置中,和連接數(shù)相關(guān)的參數(shù)有: maxThreads: Tomcat使用線程來處理接收的每個請求。這個值表示Tomcat可創(chuàng)建的最大的線程數(shù)。默認(rèn)值200。 acceptCount: 指定當(dāng)所有可以使用的處理請求的線程數(shù)都被使用時,可以放到處理隊列中的請求數(shù),超過這個數(shù)的請求將不予處理。默認(rèn)值10。 minSpareThreads: Tomcat初始化時創(chuàng)建的線程數(shù)。默認(rèn)值4。 maxSpareThreads: 一旦創(chuàng)建的線程超過這個值,Tomcat就會關(guān)閉不再需要的socket線程。默認(rèn)值50。 enableLookups: 是否反查域名,默認(rèn)值為true。為了提高處理能力,應(yīng)設(shè)置為false connnectionTimeout: 網(wǎng)絡(luò)連接超時,默認(rèn)值60000,單位:毫秒。設(shè)置為0表示永不超時,這樣設(shè)置有隱患的。通??稍O(shè)置為30000毫秒。 maxKeepAliveRequests: 保持請求數(shù)量,默認(rèn)值100。 bufferSize: 輸入流緩沖大小,默認(rèn)值2048 bytes。 compression: 壓縮傳輸,取值on/off/force,默認(rèn)值off。 其中和最大連接數(shù)相關(guān)的參數(shù)為maxThreads和acceptCount。如果要加大并發(fā)連接數(shù),應(yīng)同時加大這兩個參數(shù)。web server允許的最大連接數(shù)還受制于操作系統(tǒng)的內(nèi)核參數(shù)設(shè)置,通常Windows是2000個左右,Linux是1000個左右。 3、tomcat中如何禁止和允許列目錄下的文件 在{tomcat_home}/conf/web.xml中,把listings參數(shù)設(shè)置成false即可,如下: <servlet> ... <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> ... </servlet> 4、tomcat中如何禁止和允許主機(jī)或IP地址訪問 <Host name="localhost" ...> ... <Valve className="org.apache.catalina.valves.RemoteHostValve" allow="*.mycompany.com,[url]www.yourcompany.com[/url]"/> <Valve className="org.apache.catalina.valves.RemoteAddrValve" deny="192.168.1.*"/> ... </Host> 這是我們服務(wù)器的配置,具體文件不能給你,因為涉及到公司機(jī)密,所以那一行我給你,我拷貝下來了 JAVA_OPTS='-server -Xms512m -Xmx768m -XX:NewSize=128m -XX:MaxNewSize=192m -XX:SurvivorRatio=8' 2007-6-19 23:38 51video TOMCAT內(nèi)存和連接數(shù)配置(轉(zhuǎn)) 如果是使用的catalina.sh(linux)或Catalina.bat(win)啟動的: 修改這兩個文件,加上下面這句: SET CATALINA_OPTS= -Xms64m -Xmx128m 如果使用的winnt服務(wù)啟動: 打開C:\WINNT\system32\regedt32.exe,在HKEY_LOCAL_MACHINE-->SOFTWARE-->Apache Software Foundation-->Process Runner 1.0-->Tomcat5-->Parameters 修改屬性: -Xms64m -Xmx128m 有人建議Xms和Xmx的值取成一樣比較好,說是可以加快內(nèi)存回收速度。 加大tomcat連接數(shù): 在tomcat配置文件server.xml中的配置中,和連接數(shù)相關(guān)的參數(shù)有: minProcessors:最小空閑 maxProcessors:最大連接線程數(shù),即:并發(fā)處理的最大請求數(shù),默認(rèn)值為75 acceptCount:允許的最大連接數(shù),應(yīng)大于等于maxProcessors,默認(rèn)值為100 enableLookups:是否反查域名,取值為:true或false。為了提高處理能力,應(yīng)設(shè)置為false connectionTimeout:網(wǎng)絡(luò)連接超時,單位:毫秒。設(shè)置為0表示永不超時,這樣設(shè)置有隱患的。通常可設(shè)置為30000毫秒。 其中和最大連接數(shù)相關(guān)的參數(shù)為maxProcessors和acceptCount。如果要加大并發(fā)連接數(shù),應(yīng)同時加大這兩個參數(shù)。 web server允許的最大連接數(shù)還受制于操作系統(tǒng)的內(nèi)核參數(shù)設(shè)置,通常Windows是2000個左右,Linux是1000個左右。 |
|
來自: scorpio365 > 《java》