【高併發】高併發環境下如何防止 Tomcat 記憶體溢位?看完我懂了!!

大家好,我是冰河~~
隨著系統併發量越來越高,Tomcat 所佔用的記憶體就會越來越大,如果對 Tomcat 的記憶體管理不當,則可能會引發 Tomcat 記憶體溢位的問題,那麼,如何防止 Tomcat 記憶體溢位呢?我們今天就來一起探討下這個問題。
防止 Tomcat 記憶體溢位可以總結為兩個方案:一個是設定 Tomcat 啟動的初始記憶體,一個是防止 Tomcat 所用的 JVM 記憶體溢位。接下來,我們就分別對這兩種方案作出簡單的介紹。
設定啟動初始記憶體
其初始空間(即-Xms)是實體記憶體的 1/64,最大空間(-Xmx)是實體記憶體的 1/4。可以利用 JVM 提供的-Xmn -Xms -Xmx 等選項可進行設定。
例項
以下給出 1G 記憶體環境下 java jvm 的引數設定參考:
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -Djava.awt.headless=true "
JAVA_OPTS="-server -Xms768m -Xmx768m -XX:PermSize=128m -XX:MaxPermSize=256m -XX:NewSize=192m -XX:MaxNewSize=384m"
CATALINA_OPTS="-server -Xms768m -Xmx768m -XX:PermSize=128m -XX:MaxPermSize=256m -XX:NewSize=192m -XX:MaxNewSize=384m"
複製程式碼
Linux
在/usr/local/apache-tomcat-7.0/bin 目錄下的 catalina.sh 檔案中,新增:JAVA_OPTS='-Xms512m -Xmx1024m',要加“m”說明是 MB,否則就是 KB 了,在啟動 tomcat 時會報記憶體不足。
-
-Xms:初始值
-
-Xmx:最大值
-
-Xmn:最小值
Windows
在 catalina.bat 最前面加入 set JAVA_OPTS=-Xms128m -Xmx350m,如果用 startup.bat 啟動 tomcat,OK 設定生效。夠成功的分配 200M 記憶體。但是如果不是執行 startup.bat 啟動 tomcat 而是利用 windows 的系統服務啟動 tomcat 服務,上面的設定就不生效了,就是說 set JAVA_OPTS=-Xms128m -Xmx350m 沒起作用。上面分配 200M 記憶體就 OOM 了。。windows 服務執行的是 bin\tomcat.exe。它讀取登錄檔中的值,而不是 catalina.bat 的設定。
解決辦法
修改登錄檔
HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Tomcat Service Manager\Tomcat5\Parameters\JavaOptions
複製程式碼
原值為
-Dcatalina.home="C:\ApacheGroup\Tomcat 7.0"
-Djava.endorsed.dirs="C:\ApacheGroup\Tomcat 7.0\common\endorsed"
-Xrs
複製程式碼
加入 -Xms300m -Xmx350m 重起 tomcat 服務,設定生效。
防止所用的 JVM 記憶體溢位
1.java.lang.OutOfMemoryError: Java heap space
解釋
Heap size 設定
JVM 堆的設定是指 java 程式執行過程中 JVM 可以調配使用的記憶體空間的設定.JVM 在啟動的時候會自動設定 Heap size 的值,其初始空間(即-Xms)是實體記憶體的 1/64,最大空間(-Xmx)是實體記憶體的 1/4。可以利用 JVM 提供的-Xmn -Xms -Xmx 等選項可進行設定。Heap size 的大小是 Young Generation 和 Tenured Generaion 之和。
提示:在 JVM 中如果 98%的時間是用於 GC 且可用的 Heap size 不足 2%的時候將丟擲此異常資訊。
提示:Heap Size 最大不要超過可用實體記憶體的 80%,一般的要將-Xms 和-Xmx 選項設定為相同,而-Xmn 為 1/4 的-Xmx 值。
解決方法
手動設定 Heap size
修改 TOMCAT_HOME/bin/catalina.bat,在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下程式碼。
set JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m -XX:MaxNewSize=256m
set JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m -XX:MaxNewSize=256m
複製程式碼
或修改 catalina.sh
在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:
JAVA_OPTS="$JAVA_OPTS -server -Xms800m -Xmx800m -XX:MaxNewSize=256m"
複製程式碼
2.java.lang.OutOfMemoryError: PermGen space
原因
PermGen space 的全稱是 Permanent Generation space,是指記憶體的永久儲存區域,這塊記憶體主要是被 JVM 存放 Class 和 Meta 資訊的,Class 在被 Loader 時就會被放到 PermGen space 中,它和存放類例項(Instance)的 Heap 區域不同,GC(Garbage Collection)不會在主程式執行期對 PermGen space 進行清理,所以如果你的應用中有很 CLASS 的話,就很可能出現 PermGen space 錯誤,這種錯誤常見在 web 伺服器對 JSP 進行 pre compile 的時候。如果你的 WEB APP 下都用了大量的第三方 jar, 其大小超過了 jvm 預設的大小(4M)那麼就會產生此錯誤資訊了。
解決方法
手動設定 MaxPermSize 大小
修改 TOMCAT_HOME/bin/catalina.bat(Linux 下為 catalina.sh),在程式碼“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:
set JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=128M -XX:MaxPermSize=512m
複製程式碼
“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:
set JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=128M -XX:MaxPermSize=512m
複製程式碼
catalina.sh 檔案的修改如下。
Java 程式碼
JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=128M -XX:MaxPermSize=512m"
複製程式碼
3.分析 java.lang.OutOfMemoryError: PermGen space
發現很多人把問題歸因於: spring,hibernate,tomcat,因為他們動態產生類,導致 JVM 中的 permanent heap 溢位 。然後解決方法眾說紛紜,有人說升級 tomcat 版本到最新甚至乾脆不用 tomcat。還有人懷疑 spring 的問題,在 spring 論壇上討論很激烈,因為 spring 在 AOP 時使用 CBLIB 會動態產生很多類。
但問題是為什麼這些王牌的開源會出現同一個問題呢,那麼是不是更基礎的原因呢?tomcat 在 Q&A 很隱晦的回答了這一點,我們知道這個問題,但這個問題是由一個更基礎的問題產生。
於是有人對更基礎的 JVM 做了檢查,發現了問題的關鍵。原來 SUN 的 JVM 把記憶體分了不同的區,其中一個就是 permenter 區用來存放用得非常多的類和類描述。本來 SUN 設計的時候認為這個區域在 JVM 啟動的時候就固定了,但他沒有想到現在動態會用得這麼廣泛。而且這個區域有特殊的垃圾收回機制,現在的問題是動態載入類到這個區域後,gc 根本沒辦法回收!
對於以上兩個問題,我的處理是:
在 catalina.bat 的第一行增加:
set JAVA_OPTS=-Xms64m -Xmx256m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m
複製程式碼
在 catalina.sh 的第一行增加:
JAVA_OPTS= -Xms64m -Xmx256m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m
複製程式碼
寫在最後
如果覺得文章對你有點幫助,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習高併發程式設計技術。
最後,附上併發程式設計需要掌握的核心技能知識圖,祝大家在學習併發程式設計時,少走彎路。

劃線
評論
複製
- “打臉”谷歌雲,說好的超低延遲和可靠性呢?
- 過去的十五年,我們怎樣做 IM?
- 轉轉 K8s 實踐:如何解決容器化帶來的四大問題
- 企業級證券業務中臺探索與實踐
- 雲原生(十八) | Kubernetes 篇之 Kubernetes(k8s)工作負載
- 【原始碼解析】MyBatis 整體架構與原始碼解析
- 見微知著,帶你認認資料分析的大門,站在門口感受一下預測的魅力
- 未來是國產作業系統的鑽石時代,微核心將成為新的發展方向|對話中興新支點作業系統崔黎明
- 【React 原始碼系列】React Hydrate 原理及原始碼剖析
- 花 31 萬元重新設計網站後,我後悔了
- 一文帶你打通 Node 流的"任督二脈"
- RT-Thread 記錄(七、IPC 機制之郵箱、訊息佇列)
- 開發人員應該知道的零信任模型
- 大佬,還記得設計模式的六大設計原則嗎?
- Java 引數傳遞到底是按 值傳遞 還是 引用傳遞 ?
- 資料技術大融合,HSTAP 資料庫有多少想象空間?
- 說了半天跨平臺,今兒咱就來跨跨!(完結篇)——Kubernetes 上手實踐
- 我認為前端的職責可能需要重新劃分
- 使用 External Secrets Operator 管理 Kubernetes 的 Secret
- Android 應用安全機制實現方案探究