最近我在我的web应用程序中遇到了这个错误:
java.lang.OutOfMemoryError: PermGen space
这是一个典型的Hibernate/JPA + IceFaces/JSF应用程序,在Tomcat 6和JDK 1.6上运行。 显然,这可能会在多次重新部署应用程序后发生。
这是什么原因造成的?如何避免它?如何解决这个问题?
最近我在我的web应用程序中遇到了这个错误:
java.lang.OutOfMemoryError: PermGen space
这是一个典型的Hibernate/JPA + IceFaces/JSF应用程序,在Tomcat 6和JDK 1.6上运行。 显然,这可能会在多次重新部署应用程序后发生。
这是什么原因造成的?如何避免它?如何解决这个问题?
给Tomcat分配更多的内存不是正确的解决方案。
正确的解决方案是在上下文被销毁和重建(热部署)后进行清理。解决方案是停止内存泄漏。
如果您的Tomcat/Webapp服务器告诉您无法注销驱动程序(JDBC),则注销它们。这将停止内存泄漏。
您可以创建一个ServletContextListener并在web.xml中进行配置。以下是一个样本ServletContextListener:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.log4j.Logger;
import com.mysql.jdbc.AbandonedConnectionCleanupThread;
/**
*
* @author alejandro.tkachuk / calculistik.com
*
*/
public class AppContextListener implements ServletContextListener {
private static final Logger logger = Logger.getLogger(AppContextListener.class);
@Override
public void contextInitialized(ServletContextEvent arg0) {
logger.info("AppContextListener started");
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
logger.info("AppContextListener destroyed");
// manually unregister the JDBC drivers
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
logger.info(String.format("Unregistering jdbc driver: %s", driver));
} catch (SQLException e) {
logger.info(String.format("Error unregistering driver %s", driver), e);
}
}
// manually shutdown clean up threads
try {
AbandonedConnectionCleanupThread.shutdown();
logger.info("Shutting down AbandonedConnectionCleanupThread");
} catch (InterruptedException e) {
logger.warn("SEVERE problem shutting down AbandonedConnectionCleanupThread: ", e);
e.printStackTrace();
}
}
}
在这里你可以在web.xml中配置它:
<listener>
<listener-class>
com.calculistik.mediweb.context.AppContextListener
</listener-class>
</listener>
据说最新版本的Tomcat(6.0.28或6.0.29)能更好地处理重新部署Servlets的任务。
-XX:+CMSClassUnloadingEnabled
默认情况下,此选项设置为false。如果要启用此选项,需要在Java选项中显式设置以下选项。如果启用CMSClassUnloadingEnabled,则GC将扫描PermGen并删除不再使用的类。请记住,只有在使用以下选项启用UseConcMarkSweepGC时,此选项才起作用。因此,在运行ParallelGC或Serial GC时,请确保通过指定以下内容将GC设置为CMS:
-XX:+UseConcMarkSweepGC
内存配置取决于您的应用程序的性质。
你在做什么?
处理的交易量是多少?
加载了多少数据?
等等。
也许您可以对应用程序进行分析并开始清理一些模块。
显然,这可能会在重新部署应用程序几次后发生
Tomcat具有热部署功能,但它会消耗内存。尝试定期重启容器。此外,您需要知道运行生产模式所需的内存量,这似乎是进行研究的好时机。
我唯一成功的方法是使用JRockit JVM。我有MyEclipse 8.6。
JVM的堆存储了运行Java程序生成的所有对象。Java使用new
操作符创建对象,新对象的内存在运行时分配到堆中。垃圾回收是自动释放被程序不再引用的对象所包含的内存的机制。
如果您在eclipse IDE中设置了参数--launcher.XXMaxPermSize
, -XX:MaxPermSize
等,但仍然遇到相同的错误,那么很可能是eclipse正在使用由某些第三方应用程序安装并设置为默认的有缺陷的JRE版本。这些有缺陷的版本不会选择PermSize参数,因此无论您设置什么,仍然会出现内存错误。因此,请在您的eclipse.ini文件中添加以下参数:
-vm <path to the right JRE directory>/<name of javaw executable>
同时,请确保您在Eclipse的首选项中设置了正确版本的Java作为默认JRE。