最近,我在我的web应用程序中遇到了这个错误:
java.lang.OutOfMemoryError:永久生成空间
它是一个典型的Hibernate/JPA + IceFaces/JSF应用程序,运行在Tomcat 6和JDK 1.6上。 显然,这可能发生在重新部署应用程序几次之后。
是什么原因导致的,如何避免呢? 我该如何解决这个问题?
最近,我在我的web应用程序中遇到了这个错误:
java.lang.OutOfMemoryError:永久生成空间
它是一个典型的Hibernate/JPA + IceFaces/JSF应用程序,运行在Tomcat 6和JDK 1.6上。 显然,这可能发生在重新部署应用程序几次之后。
是什么原因导致的,如何避免呢? 我该如何解决这个问题?
当前回答
First step in such case is to check whether the GC is allowed to unload classes from PermGen. The standard JVM is rather conservative in this regard – classes are born to live forever. So once loaded, classes stay in memory even if no code is using them anymore. This can become a problem when the application creates lots of classes dynamically and the generated classes are not needed for longer periods. In such a case, allowing the JVM to unload class definitions can be helpful. This can be achieved by adding just one configuration parameter to your startup scripts:
-XX:+CMSClassUnloadingEnabled
默认情况下,它被设置为false,因此要启用它,您需要显式地在Java选项中设置以下选项。如果你启用了CMSClassUnloadingEnabled, GC也会扫描PermGen并删除不再使用的类。请记住,此选项仅在UseConcMarkSweepGC也使用下面的选项启用时才有效。因此,当运行ParallelGC或串行GC时,请确保您已经通过指定将GC设置为CMS:
-XX:+UseConcMarkSweepGC
其他回答
我尝试了几个答案,最终完成工作的唯一一件事是在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=128M而不是-XX:MaxPermGen=128M。
我不能确切地说出这个内存池的使用情况,但它与装入JVM中的类的数量有关。(因此,为tomcat启用类卸载可以解决这个问题。)如果您的应用程序在运行时生成和编译类,则更可能需要比默认值更大的内存池。
或者,你可以切换到JRockit,它处理permgen的方式不同于sun的jvm。它通常也有更好的性能。
http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html
对我来说,唯一有效的方法就是JRockit JVM。我有MyEclipse 8.6。
JVM的堆存储运行中的Java程序生成的所有对象。Java使用new操作符创建对象,并且在运行时在堆上为新对象分配内存。垃圾收集是一种自动释放程序不再引用的对象所包含的内存的机制。
为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>