最近,我在我的web应用程序中遇到了这个错误:

java.lang.OutOfMemoryError:永久生成空间

它是一个典型的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分配更多内存不是正确的解决方案。

正确的解决方案是在销毁和重新创建上下文(热部署)之后进行清理。解决方案是停止内存泄漏。

如果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>  

outofmemoryerror: PermGen space消息表示内存中的永久生成区域已耗尽。

任何Java应用程序都允许使用有限的内存。特定应用程序可以使用的确切内存量是在应用程序启动时指定的。

Java内存被划分为不同的区域,如下图所示:

Metaspace:一个新的内存空间诞生

JDK 8 HotSpot JVM现在使用本机内存来表示类元数据,被称为Metaspace;类似于Oracle的JRockit和IBM的JVM。

好消息是,这意味着不再存在java.lang.OutOfMemoryError: PermGen空间问题,也不再需要使用Java_8_Download或更高版本来调优和监视这个内存空间。

我尝试了几个答案,最终完成工作的唯一一件事是在pom中的编译器插件的配置:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
        <fork>true</fork>
        <meminitial>128m</meminitial>
        <maxmem>512m</maxmem>
        <source>1.6</source>
        <target>1.6</target>
        <!-- prevent PermGen space out of memory exception -->
        <!-- <argLine>-Xmx512m -XX:MaxPermSize=512m</argLine> -->
    </configuration>
</plugin>

希望这一点能有所帮助。

尝试-XX:MaxPermSize=256m,如果仍然存在,尝试-XX:MaxPermSize=512m

我有一个Hibernate+Eclipse RCP的组合,尝试使用-XX:MaxPermSize=512m和-XX:PermSize=512m,它似乎对我有用。