Tomcat 7執行中發生了java.lang.OutOfMemoryError: PermGen space
的錯誤,也就是記憶體空間不足所導致。
解決辦法是在Tomcat安裝目錄下的bin
資料夾新增一個setenv.bat
檔(Windows系統)或setenv.sh
(Linux系統),並在裡面設定以下內容。
JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=128M -XX:MaxPermSize=512m"
上面的設定是把PermSize
及MaxPermSize
的值增加,例如設為-XX:PermSize=512M -XX:MaxPermSize=1024m
。
如果Tomcat運行的JVM版本為JDK7,則試著加入
-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC
上面設定牽涉到JDK7的永久世代記憶體區塊(Permanent Generation space)的垃圾回收(GC)機制的問題(JDK 8以後PerGem被移除,改為Metaspace),而垃圾回收的發生可能會進入STW(stop-the-world) GC而影響效能,所以使用前必須對JVN的垃圾回收器及回收機制有一定的認識。
PermGen(Permanent Generation space)是JVM中配置的一塊記憶體位置,用來存放Java Class資訊及meta資料,所以當程式中有太多的Class且PermGen的大小又不夠的話就會發生java.lang.OutOfMemoryError: PermGen space
的錯誤。
因此上述的方法分別為調高PermGen的上限,啟用CMS垃圾回收器對PermGen中的Class進行回收。
關於setenv.bat
檔請見以下引述自Tocmat的catalina.bat
中的註解
Do not set the variables in this script. Instead put them into a script setenv.bat in CATALINA_BASE/bin to keep your customizations separate.
引述Tomcat的RUNNUNG.txt
說明:
(3.4) Using the "setenv" script (optional, recommended)
Apart from CATALINA_HOME and CATALINA_BASE, all environment variables can be specified in the "setenv" script. The script is placed either into CATALINA_BASE/bin or into CATALINA_HOME/bin directory and is named setenv.bat (on Windows) or setenv.sh (on *nix). The file has to be readable.
By default the setenv script file is absent. If the script file is present both in CATALINA_BASE and in CATALINA_HOME, the one in CATALINA_BASE is preferred.
...
The CATALINA_HOME and CATALINA_BASE variables cannot be configured in the setenv script, because they are used to locate that file.
All the environment variables described here and the "setenv" script are used only if you use the standard scripts to launch Tomcat. For example, if you have installed Tomcat as a service on Windows, the service wrapper launches Java directly and does not use the script files.
下面程式模擬java.lang.OutOfMemoryError: PermGen space
的錯誤。
程式中有用到Javassist函式庫的ClassPool
類,可以透過在Maven的pom.xml
加入下面依賴
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
import java.util.concurrent.TimeUnit;
import javassist.CannotCompileException;
import javassist.ClassPool;
public class App {
/**
* 這個static block是用來先初始化OutOfMemoryError,
* 因為真正發生java.lang.OutOfMemoryError: PermGen space 時,
* 已經沒有記憶體來建立OutOfMemoryError了,所以要先初始化
*/
static {
new OutOfMemoryError().printStackTrace();
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("=====================");
System.out.println("建立OutOfMemoryError");
System.out.println("=====================");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
System.out.println("開始動態產生Class.....\n");
for (int i = 0; i < 100000; i++) {
Class clazz = createClass("MyClass" + i);
// 觀察大約有多少個Class被建立
if(i % 50 == 0) System.out.println(clazz);
}
}
private static Class createClass(String className) throws CannotCompileException {
ClassPool pool = ClassPool.getDefault();
return pool.makeClass(className).toClass();
}
}
- About G1 Garbage Collector, Permanent Generation and Metaspace
- Dealing with “java.lang.OutOfMemoryError: PermGen space” error
- What does JVM flag CMSClassUnloadingEnabled actually do?
- G1 garbage collector: Perm Gen fills up indefinitely until a Full GC is performed
- The 4 Java Garbage Collectors - How the Wrong Choice Dramatically Impacts Performance
- Presenting the Permanent Generation
- Concurrent Mark Sweep (CMS) Collector
- Java 什麼是GC (Garbage Collection)
沒有留言:
張貼留言