Tomcat的JVM配置:解決Out of memory:java head space

    之前開發中遇見了一個讓人很頭疼的問題,java.lang.OutOfMemoryError 棧內存溢出。主要發生情況爲,在本機開啓服務器測試完全沒有發生任何錯誤,但是當部署到客戶服務器上時,就會發生這個錯誤,同樣的代碼,同樣的數據庫結構,以及同樣的數據,在兩個不同環境下運行會發生不同的結果,所以暫時斷定與代碼無關。

    首先我去網上查了一下這個錯誤,網上說以下幾種情況會引起內存溢出:

  1. 內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;
  2. 集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;
  3. 代碼中存在死循環或循環產生過多重複的對象實體;
  4. 使用的第三方軟件中的BUG;
  5. 啓動參數內存值設定的過小;
    因爲之前已經排除代碼的錯誤,以及後臺沒有使用第三方軟件,所以第三種和第四種情況可以無視掉。那麼,我便開始從第一種情況排查。首先,我懷疑服務器上的數據庫可能與我自己電腦中的數據庫版本不同或者配置有差異,例如:本機數據庫與服務器上的數據庫支持一次性操作的數據量不同,可能是本機的數據操作量比較大,所以在本機上沒有錯誤,而服務器的數據庫可一次性操作數據量小,所以內存溢出?

    於是我將後端所有一次操作大量數據的查詢接口全部分頁處理,例如:一次性查詢1000條的數據,改爲一次查詢100條,查詢10次。經過一系列的修改後,再次使用服務器來運行,問題竟然解決了,不再出現內存溢出情況。

    我以爲事情到這裏就結束了,萬萬沒想到,大概半個月之後,這個錯誤竟然又一次出現了。那麼,基於上邊所說的五種情況,我們已經排除掉了三種。看到第二條,即:集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;因爲上次修改分頁之後當時是沒有再出現問題的,問題出現的時候是在半個月之後,所以我想,可能是後端代碼中有的地方對象使用過後沒有清空,從而多次長時間調用,會一點一點佔用JVM的內存,時間一長,將JVM內存佔用飽和,所以纔會再半個月後又再次發生錯誤?

    然後我排查了一遍所有後端代碼,但是並沒有發現不會被回收的情況。因爲我們項目使用的是標準的SSM框架,所以後端代碼的結構一目瞭然,並沒有全局變量沒有被回收的情況,所有的全局變量都是通過spring MVC 註解形式注入。JVM虛擬機的回收機制中,在結束變量所在作用域後,就會被JVM自主回收,所以我將第二種情況也排除掉了。

    那麼事情就變得簡單了,只剩下最後一種情況了:JVM分配的內存空間太小而導致內存溢出。

    由此在度娘下,找到了tomcat的一種查看內存狀態的方法;

    打開tomcat文件目錄下的這個位置:E:\apache-tomcat-7.0.52\conf在conf文件夾下編輯tomcat-users.xml文件,將</tomcat-users>之前  <!.. ..> that surrounds them.-->之後的代碼註釋解開,並改爲

<role rolename="tomcat"/>
  <role rolename="role1"/>
 <role rolename="manage-gui"/>
 <role rolename="admin-gui"/>
  <user username="tomcat" password="qwe123" roles="manager-gui"/>
  <user username="both" password="qwe123" roles="tomcat,role1"/>
  <user username="admin" password="qwe123" roles="manager-gui"/>

password項自己設置,此爲多個權限不同的賬號,我們用到的是最後一個。

然後啓動服務器,在瀏覽器中輸入http://服務器的IP地址:端口號/

例如:http://192.168.2.111:8080/

打開後頁面:



點擊server status 就會看到JVM的內存情況,但是這是非實時動態顯示的,需要不停的刷新來觀察:



左上角顯示JVM的最大內存,最小內存,與空閒內存,當空閒內存不足時,便會內存溢出,而我們避免這種情況發生的話就需要修改最大值和最小值來擴大JVM的內存。

    

    那麼好吧,要怎麼修改呢?

    打開tomcat中的bin文件夾:路徑爲:E:\apache-tomcat-7.0.52\bin。點擊編輯bin下的catalina.bat文件:



在最上方位置加入你要爲JVM指定內存空間大小的配置:


    

四個參數分別代表:

-Xms JVM初始分配的堆內存
-Xmx JVM最大允許分配的堆內存,按需分配
-XX:PermSize JVM初始分配的非堆內存
-XX:MaxPermSize JVM最大允許分配的非堆內存,按需分配

按照需求來分配就可以了,但是又發現一種情況讓我非常無奈,tomcat7.0 有兩種版本:一種是如上所述,使用startup.bat來開啓,用shutdown.bat來關閉的,大多數的都是這個版本,包括我自己的電腦也是。但是!在客戶的服務器上tomcat的bin文件夾下是這樣的:



    這就非常尷尬了,沒有catalina.bat文件,要怎麼配置JVM內存?

    好吧,經過兩個多小時的百度,終於在一篇不太起眼的大神文章中找到了解決辦法:利用註冊表來修改JVM內存配置,來看看具體步驟吧:

    首先用命令方式打開註冊表:





win7 32位系統如下:

HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Procrun 2.0\tomcat8\Parameters\Java

Win7 X64系統則位於

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Apache Software Foundation\Procrun 2.0\tomcat8\Parameters\Java


    右鍵jvmMs編輯JVM初始分配的堆內存:



    同樣的方法設置jvmMx最大允許分配的堆內存,分配PermSize和MaxPermSize右鍵編輯options:




    這樣重啓tomcat後在上述頁面中查看JVM內存狀態,就會發現內存已經變更成功了!

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