之前開發中遇見了一個讓人很頭疼的問題,java.lang.OutOfMemoryError 棧內存溢出。主要發生情況爲,在本機開啓服務器測試完全沒有發生任何錯誤,但是當部署到客戶服務器上時,就會發生這個錯誤,同樣的代碼,同樣的數據庫結構,以及同樣的數據,在兩個不同環境下運行會發生不同的結果,所以暫時斷定與代碼無關。
首先我去網上查了一下這個錯誤,網上說以下幾種情況會引起內存溢出:
- 內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;
- 集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;
- 代碼中存在死循環或循環產生過多重複的對象實體;
- 使用的第三方軟件中的BUG;
- 啓動參數內存值設定的過小;
於是我將後端所有一次操作大量數據的查詢接口全部分頁處理,例如:一次性查詢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內存狀態,就會發現內存已經變更成功了!