Tomcat中JVM內存溢出及合理配置

Tomcat本身不能直接在計算機上運行,需要依賴於硬件基礎之上的操作系統和一個Java虛擬機。Tomcat的內存溢出本質就是JVM內存溢出,所以在本文開始時,應該先對Java JVM有關內存方面的知識進行詳細介紹。

一、Java JVM內存介紹

JVM管理兩種類型的內存,堆和非堆。按照官方的說法:“Java 虛擬機具有一個堆,堆是運行時數據區域,所有類實例和數組的內存均從此處分配。堆是在 Java 虛擬機啓動時創建的。”“在JVM中堆之外的內存稱爲非堆內存(Non-heap memory)”。簡單來說堆就是Java代碼可及的內存,是留給開發人員使用的;非堆就是JVM留給自己用的,所以方法區、JVM內部處理或優化所需的內存(如JIT編譯後的代碼緩存)、每個類結構(如運行時常數池、字段和方法數據)以及方法和構造方法的代碼都在非堆內存中,它和堆不同,運行期內GC不會釋放其空間。

(1). 堆內存分配 
JVM初始分配的內存由-Xms指定,默認是物理內存的1/64;JVM最大分配的內存由-Xmx指 定,默認是物理內存的1/4。默認空餘堆內存小於 40%時,JVM就會增大堆直到-Xmx的最大限制;空餘堆內存大於70%時,JVM會減少堆直到-Xms的最小限制。因此服務器一般設置-Xms、 -Xmx相等以避免在每次GC 後調整堆的大小。可以利用JVM提供的-Xmn -Xms -Xmx等選項可進行堆內存設置,一般的要將-Xms和-Xmx選項設置爲相同,而-Xmn爲1/4的-Xmx值,建議堆的最大值設置爲可用內存的最大值的80%。

初始化堆的大小是JVM在啓動時向系統申請的內存的大小。一般而言,這個參數不重要。但是有的應用程序在大負載的情況下會急劇地佔用更多的內存,此時這個參數就是顯得非常重要,如果JVM啓動時設置使用的內存比較小而在這種情況下有許多對象進行初始化,JVM就必須重複地增加內存來滿足使用。由於這種原因,我們一般把-Xms和-Xmx設爲一樣大,而堆的最大值受限於系統使用的物理內存。一般使用數據量較大的應用程序會使用持久對象,內存使用有可能迅速地增長。當應用程序需要的內存超出堆的最大值時JVM就會提示內存溢出,並且導致應用服務崩潰。所以,如果Xms超過了Xmx值,或者堆最大值和非堆最大值的總和超過了物理內存或者操作系統的最大限制都會引起服務器啓動不起來。

(2). 非堆內存分配 
也叫永久保存的區域,用於存放Class和Meta信息,Class在被Load的時候被放入該區域。它和存放類實例(Instance)的Heap區域不同,GC(Garbage Collection)不會在主程序運行期對PermGen space進行清理。JVM使用-XX:PermSize設置非堆內存初始值,默認是物理內存的1/64;由XX:MaxPermSize設置最大非堆內存的大小,默認是物理內存的1/4。 GC不會對PermGen space進行清理,所以如果你的APP會LOAD很多CLASS的話,就很可能出現PermGen space錯誤。

(3). JVM內存限制(最大值) 
首先JVM內存限制於實際的最大物理內存(廢話!,呵呵),假設物理內存無限大的話,JVM內存的最大值跟操作系統有很大的關係。簡單的說就32位處理器雖然可控內存空間有4GB,但是具體的操作系統會給一個限制,這個限制一般是2GB-3GB(一般來說Windows系統下爲1.5G-2G,Linux系統 下爲2G-3G),而64bit以上的處理器就不會有限制了。

二、三種內存溢出異常介紹

1. OutOfMemoryError: Java heap space  堆溢出

內存溢出主要存在問題就是出現在這個情況中。當在JVM中如果98%的時間是用於GC且可用的 Heap size 不足2%的時候將拋出此異常信息。

 2. OutOfMemoryError: PermGen space   非堆溢出(永久保存區域溢出)

這種錯誤常見在web服務器對JSP進行pre compile的時候。如果你的WEB APP下都用了大量的第三方jar, 其大小超過了jvm默認的大小(4M)那麼就會產生此錯誤信息了。如果web app用了大量的第三方jar或者應用有太多的class文件而恰好MaxPermSize設置較小,超出了也會導致這塊內存的佔用過多造成溢出,或者tomcat熱部署時侯不會清理前面加載的環境,只會將context更改爲新部署的,非堆存的內容就會越來越多。

3. OutOfMemoryError: unable to create new native thread.   無法創建新的線程

這種現象比較少見,也比較奇怪,主要是和jvm與系統內存的比例有關。這種怪事是因爲JVM已經被系統分配了大量的內存(比如1.5G),並且它至少要佔用可用內存的一半。


三、Java JVM內存配置

1. JVM內存分配設置的參數有四個

-Xmx    Java Heap最大值,默認值爲物理內存的1/4;

-Xms    Java Heap初始值,Server端JVM最好將-Xms和-Xmx設爲相同值,開發測試機JVM可以保留默認值;

-Xmn    Java Heap Young區大小,不熟悉最好保留默認值;

-Xss      每個線程的Stack大小,不熟悉最好保留默認值;

-XX:PermSize:設定內存的永久保存區域; 

-XX:MaxPermSize:設定最大內存的永久保存區域;

-XX:PermSize:設定內存的永久保存區域;

-XX:NewSize:設置JVM堆的‘新生代’的默認大小;

-XX:MaxNewSize:設置JVM堆的‘新生代’的最大大小; 

2. 如何設置JVM的內存分配

(1)當在命令提示符下啓動並使用JVM時(只對當前運行的類Test生效):

java -Xmx128m -Xms64m -Xmn32m -Xss16m Test

(2)當在集成開發環境下(如eclipse)啓動並使用JVM時:

a. 在eclipse根目錄下打開eclipse.ini,默認內容爲(這裏設置的是運行當前開發工具的JVM內存分配):  -vmargs -Xms40m -Xmx256m -vmargs表示以下爲虛擬機設置參數,可修改其中的參數值,也可添加-Xmn,-Xss,另外,eclipse.ini內還可以設置非   堆內存,如:-XX:PermSize=56m,-XX:MaxPermSize=128m。

b. 打開eclipse-窗口-首選項-Java-已安裝的JRE(對在當前開發環境中運行的java程序皆生效)  編輯當前使用的JRE,在缺省VM參數中輸入:-Xmx128m -Xms64m -Xmn32m –Xss16m。

c. 打開eclipse-運行-運行-Java應用程序(只對所設置的java類生效)  選定需設置內存分配的類-自變量,在VM自變量中輸入:-Xmx128m -Xms64m -Xmn32m -Xss16m  注:如果在同一開發環境中同時進行了b和c設置,則b設置生效,c設置無效,如:  開發環境的設置爲:-Xmx256m,而類Test的設置爲:-Xmx128m -Xms64m,則運行Test時生效的設置爲:  -Xmx256m -Xms64m。

(3)當在服務器環境下(如Tomcat)啓動並使用JVM時(對當前服務器環境下所以Java程序生效):

a. 設置環境變量:  變量名:CATALINA_OPTS  變量值:-Xmx128m -Xms64m -Xmn32m -Xss16m。

b. 打開Tomcat根目錄下的bin文件夾,編輯catalina.bat,將其中的%CATALINA_OPTS%(共有四處)替換爲:-Xmx128m -Xms64m -Xmn32m -Xss16m。

c. 若沒有catalina.bat,只有tomcat.exe,tomcat6w.exe;則可以在啓動tomcat6w.exe 後 右鍵配置--Java--java option 下面輸入:

-Xmx256m –Xms64m

也可以找到註冊表HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\TomcatService Manager\Tomcat6\Parameters\JavaOptions原值爲 -Dcatalina.home="C:\ApacheGroup\Tomcat 6.0" -Djava.endorsed.dirs="C:\ApacheGroup\Tomcat 6.0\common\endorsed" -Xrs 加入  -Xms300m  -Xmx350m  (我的是加入-Xmx350m,tomcat才能啓動,加入-Xms300m  -Xmx350m反而tomcat都不能啓動)重起tomcat服務,設置生效。

3. 查看JVM內存信息

Runtime.getRuntime().maxMemory(); //最大可用內存,對應-Xmx 

Runtime.getRuntime().freeMemory(); //當前JVM空閒內存 

Runtime.getRuntime().totalMemory(); //當前JVM佔用的內存總數,其值相當於當前JVM已使用的內存及freeMemory()的總和 

關於maxMemory(),freeMemory()和totalMemory():maxMemory()爲JVM的最大可用內存,可通過-Xmx設置,默認值爲物理內存的1/4,設置不能高於計算機物理內存;  totalMemory()爲當前JVM佔用的內存總數,其值相當於當前JVM已使用的內存及freeMemory()的總和,會隨着JVM使用內存的增加而增加;  freeMemory()爲當前JVM空閒內存,因爲JVM只有在需要內存時才佔用物理內存使用,所以freeMemory()的值一般情況下都很小,而JVM實際可用內存並不等於freeMemory(),而應該等於maxMemory()-totalMemory()+freeMemory()。

4. 實例,以下給出1G內存環境下java jvm 的參數設置參考

JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -Djava.awt.headless=true "

大型的web工程,用tomcat默認分配的內存空間無法啓動,如果不是在myeclipse中啓動tomcat可以對tomcat這樣設置:

TOMCAT_HOME\bin\catalina.bat 中添加這樣一句話:

set JAVA_OPTS= -Xmx1024M -Xms512M -XX:MaxPermSize=256m

如果要在myeclipse中啓動,上述的修改就不起作用了,可如下設置:

Myeclipse->preferences->myeclipse->servers->tomcat->tomcat×.×->JDK面板中的

Optional Java VM arguments中添加:-Xmx1024M -Xms512M -XX:MaxPermSize=256m

對於單獨的.class,可以用下面的方法對Test運行時的jvm內存進行設置。 java -Xms64m -Xmx256m Test -Xms是設置內存初始化的大小 -Xmx是設置最大能夠使用內存的大小。

四、JVM內存配置與GC

需要考慮的是Java提供的垃圾回收機制。JVM的堆大小決定了JVM花費在收集垃圾上的時間和頻度。收集垃圾可以接受的速度與應用有關,應該通過分析實際的垃圾收集的時間和頻率來調整。如果堆的大小很大,那麼完全垃圾收集就會很慢,但是頻度會降低。如果你把堆的大小和內存的需要一致,完全收集就很快,但是會更加頻繁。調整堆大小的的目的是最小化垃圾收集的時間,以在特定的時間內最大化處理客戶的請求。在基準測試的時候,爲保證最好的性能,要把堆的大小設大,保證垃圾收集不在整個基準測試的過程中出現。如果系統花費很多的時間收集垃圾,請減小堆大小。一次完全的垃圾收集應該不超過 3-5 秒。如果垃圾收集成爲瓶頸,那麼需要指定堆的大小,檢查垃圾收集的詳細輸出,研究垃圾收集參數對性能的影響。一般說來,你應該使用物理內存的 80% 作爲堆大小。當增加處理器時,記得增加內存,因爲分配可以並行進行,而垃圾收集不是並行的。

Java Heap分爲3個區:

1.Young 2.Old 3.Permanent。Young保存剛實例化的對象。當該區被填滿時,GC會將對象移到Old區。Permanent區則負責保存反射對象,本文不討論該區。

JVM有2個GC線程: 
第一個線程負責回收Heap的Young區; 
第二個線程在Heap不足時,遍歷Heap,將Young 區升級爲Older區,Older區的大小等於-Xmx減去-Xmn,不能將-Xms的值設的過大,因爲第二個線程被迫運行會降低JVM的性能。

爲什麼一些程序頻繁發生GC?有如下原因: 
1. 程序內調用了System.gc()或Runtime.gc()。 
2. 一些中間件軟件調用自己的GC方法,此時需要設置參數禁止這些GC。 
3. Java的Heap太小,一般默認的Heap值都很小。 
4. 頻繁實例化對象,Release對象 此時儘量保存並重用對象,例如使用StringBuffer()和String()。

如果你發現每次GC後,Heap的剩餘空間會是總空間的50%,這表示你的Heap處於健康狀態許多Server端的Java程序每次GC後最好能有65%的剩餘空間。

經驗之談: 
1.Server端JVM最好將-Xms和-Xmx設爲相同值。爲了優化GC,最好讓-Xmn值約等於-Xmx的1/3。 
2.一個GUI程序最好是每10到20秒間運行一次GC,每次在半秒之內完成。

注意: 
1.增加Heap的大小雖然會降低GC的頻率,但也增加了每次GC的時間。並且GC運行時,所有的用戶線程將暫停,也就是GC期間,Java應用程序不做任何工作。
2.Heap大小並不決定進程的內存使用量。進程的內存使用量要大於-Xmx定義的值,因爲Java爲其他任務分配內存,例如每個線程的Stack等。

出處http://blog.csdn.net/ye1992/article/details/9344807


我的tomcat 優化配置

  1. 配置server.xml



  2. <?xml version='1.0' encoding='utf-8'?>
    <!--
      Licensed to the Apache Software Foundation (ASF) under one or more
      contributor license agreements.  See the NOTICE file distributed with
      this work for additional information regarding copyright ownership.
      The ASF licenses this file to You under the Apache License, Version 2.0
      (the "License"); you may not use this file except in compliance with
      the License.  You may obtain a copy of the License at
    
          http://www.apache.org/licenses/LICENSE-2.0
    
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
    -->
    <!-- Note:  A "Server" is not itself a "Container", so you may not
         define subcomponents such as "Valves" at this level.
         Documentation at /docs/config/server.html
     -->
    <Server port="8005" shutdown="SHUTDOWN">
    
      <!--APR library loader. Documentation at /docs/apr.html -->
      <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
      <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
      <Listener className="org.apache.catalina.core.JasperListener" />
      <!-- Prevent memory leaks due to use of particular java/javax APIs-->
      <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
      <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html -->
      <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
      <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
    
      <!-- Global JNDI resources
           Documentation at /docs/jndi-resources-howto.html
      -->
      <GlobalNamingResources>
        <!-- Editable user database that can also be used by
             UserDatabaseRealm to authenticate users
        -->
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml" />
      </GlobalNamingResources>
    
      <!-- A "Service" is a collection of one or more "Connectors" that share
           a single "Container" Note:  A "Service" is not itself a "Container", 
           so you may not define subcomponents such as "Valves" at this level.
           Documentation at /docs/config/service.html
       -->
      <Service name="Catalina">
      
        <!--The connectors can use a shared executor, you can define one or more named thread pools-->
        <!--
        <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" 
            maxThreads="150" minSpareThreads="4"/>
        -->
        
        
        <!-- A "Connector" represents an endpoint by which requests are received
             and responses are returned. Documentation at :
             Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
             Java AJP  Connector: /docs/config/ajp.html
             APR (HTTP/AJP) Connector: /docs/apr.html
             Define a non-SSL HTTP/1.1 Connector on port 8080
        -->
        <Connector port="8080" protocol="HTTP/1.1" 
               
    			    keepAliveTimeout="15000"
    
    			    maxKeepAliveRequests="1"
    			
    				URIEncoding="UTF-8" 
    			
    			    maxHttpHeaderSize="8192"
    
                    minProcessors="100"
    
                    maxProcessors="5000"
    
                    maxThreads="5000"
    
                    minSpareThreads="1000"
    
                    maxSpareThreads="4000"
    
                    enableLookups="false"
    
                    acceptCount="3500"
    
                    disableUploadTimeout="true"
    
                    connectionTimeout="20000"
    
                    debug="0"
                redirectPort="8443" />
    
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    
    
      
        <Engine name="Catalina" defaultHost="localhost">
    
          <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                 resourceName="UserDatabase"/>
    
          <Host name="localhost"  appBase="webapps"
                unpackWARs="true" autoDeploy="true"
                xmlValidation="false" xmlNamespaceAware="false">
    
         
          </Host>
        </Engine>
      </Service>
    
    
    
       <Service name="Catalina1">
      
      
        <Engine name="Catalina1" defaultHost="localhost">
    
         
          <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                 resourceName="UserDatabase"/>
    
          <!-- Define the default virtual host
               Note: XML Schema validation will not work with Xerces 2.2.
           -->
          <Host name="localhost"  appBase="webapps1"
                unpackWARs="true" autoDeploy="true"
                xmlValidation="false" xmlNamespaceAware="false">
    
          
          </Host>
        </Engine>
      </Service>
    </Server>
  3. jvm配置 


    Freememory: 452.98 MB Total memory: 508.81 MB Max memory: 1020.81 MB

                  Freememory:當前可用的內存;

                Totalmemory:當前已經分配的JVM內存;

                Max memory:當前允許分配的最大JVM內存;


    環境變量添加:CATALINA_OPTS  -Xmx1024m -Xms512m -Xmn32m -Xss16m


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章