最近,我在Web应用程序中遇到此错误:
java.lang.OutOfMemoryError:PermGen空间
这是在Tomcat 6和JDK 1.6上运行的典型Hibernate / JPA + IceFaces / JSF应用程序。显然,这可能是在重新部署应用程序几次之后发生的。
是什么原因引起的,可以采取什么措施避免它发生?我该如何解决该问题?
最近,我在Web应用程序中遇到此错误:
java.lang.OutOfMemoryError:PermGen空间
这是在Tomcat 6和JDK 1.6上运行的典型Hibernate / JPA + IceFaces / JSF应用程序。显然,这可能是在重新部署应用程序几次之后发生的。
是什么原因引起的,可以采取什么措施避免它发生?我该如何解决该问题?
Answers:
解决方案是在启动Tomcat时将这些标志添加到JVM命令行:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
您可以通过关闭tomcat服务,然后进入Tomcat / bin目录并运行tomcat6w.exe来实现。在“ Java”选项卡下,将参数添加到“ Java选项”框中。单击“确定”,然后重新启动服务。
如果出现错误,则指定的服务不存在为已安装的服务,应运行:
tomcat6w //ES//servicename
其中servicename是在services.msc中查看的服务器的名称
资料来源:orx对Eric的《敏捷答案》的评论。
多次部署后发生的应用服务器PermGen错误很可能是由容器对旧应用的类加载器中的引用所引起的。例如,使用自定义日志级别类将导致引用由应用服务器的类加载器保留。您可以使用现代(JDK6 +)JVM分析工具(例如jmap和jhat)来检测这些类加载器之间的泄漏,以查看哪些类继续保留在您的应用程序中,然后重新设计或取消使用它们。通常的可疑对象是数据库,记录器和其他基础框架级别的库。
请参阅Classloader泄漏:可怕的“ java.lang.OutOfMemoryError:PermGen空间”异常,尤其是其后续文章。
人们常犯的错误是认为堆空间和permgen空间是相同的,这是完全不正确的。您可能在堆中剩余很多空间,但仍可能在permgen中用完内存。
PermGen中OutofMemory的常见原因是ClassLoader。每当将类加载到JVM中时,所有其元数据以及Classloader都将保留在PermGen区域上,并且在将它们加载到的Classloader准备好进行垃圾收集时将对它们进行垃圾收集。在Case Case发生内存泄漏的情况下,一旦重复几次,它加载的所有类将保留在内存中并导致permGen内存不足。经典示例是Tomcat中的Java.lang.OutOfMemoryError:PermGen Space。
现在有两种方法可以解决此问题:
1.查找内存泄漏的原因或是否有内存泄漏。
2.通过使用JVM param -XX:MaxPermSize
和增加PermGen Space的大小-XX:PermSize
。
您也可以在Java中查看2 Java.lang.OutOfMemoryError的解决方案以获取更多详细信息。
-XX:MaxPermSize and -XX:PermSize
?我找不到catalina.bat
。我的tomcat版本是5.5.26
。
-XX:MaxPermSize=128m
对Sun JVM 使用命令行参数(显然,用128代替所需的大小)。
尝试-XX:MaxPermSize=256m
,如果仍然存在,请尝试-XX:MaxPermSize=512m
XX:MaxPermSize=1024m
:)
我在使用eclipse ide时向VM参数添加了 -XX: MaxPermSize = 128m
(可以尝试最有效的方法)。在大多数JVM中,默认的PermSize约为64MB,如果项目中有太多的类或太多的String,则会耗尽内存。
对于日食,也可以在answer上进行描述。
步骤1:在“ 服务器”选项卡上双击tomcat服务器
步骤2:打开Conf会议,并添加-XX: MaxPermSize = 128m
到现有VM争论的结尾。
在部署和取消部署复杂的Web应用程序时,我一直对这个问题不屑一顾,并认为我会添加一个解释和解决方案。
当我在Apache Tomcat上部署应用程序时,将为该应用程序创建一个新的ClassLoader。然后,使用ClassLoader加载应用程序的所有类,并且在取消部署后,一切都应该消失了。但是,实际上并不是那么简单。
在Web应用程序生命周期中创建的一个或多个类拥有一个静态引用,该静态引用沿行的某个地方引用了ClassLoader。由于该引用最初是静态的,因此没有大量垃圾收集可以清理该引用-ClassLoader及其所有已加载的类都将保留在这里。
经过几次重新部署后,我们遇到了OutOfMemoryError。
现在,这已成为一个相当严重的问题。我可以确保在每次重新部署后都重新启动Tomcat,但这会关闭整个服务器,而不仅仅是关闭正在重新部署的应用程序,这通常是不可行的。
因此,我改为使用代码编写了一个解决方案,该解决方案可在Apache Tomcat 6.0上运行。我尚未在任何其他应用程序服务器上进行测试,并且必须强调,如果不对任何其他应用程序服务器进行修改,这很可能无法正常工作。
我还想说一句我个人讨厌此代码,并且如果可以将现有代码更改为使用适当的关闭和清除方法,则没有人应该将此作为“快速修复”。唯一应该使用的方法是,如果有一个外部库,您的代码所依赖的外部库(在我的情况下,它是RADIUS客户端)没有提供清除其自身静态引用的方法。
无论如何,在代码上。应该在应用程序未部署的位置调用此方法,例如Servlet的destroy方法或ServletContextListener的contextDestroyed方法(更好的方法)。
//Get a list of all classes loaded by the current webapp classloader
WebappClassLoader classLoader = (WebappClassLoader) getClass().getClassLoader();
Field classLoaderClassesField = null;
Class clazz = WebappClassLoader.class;
while (classLoaderClassesField == null && clazz != null) {
try {
classLoaderClassesField = clazz.getDeclaredField("classes");
} catch (Exception exception) {
//do nothing
}
clazz = clazz.getSuperclass();
}
classLoaderClassesField.setAccessible(true);
List classes = new ArrayList((Vector)classLoaderClassesField.get(classLoader));
for (Object o : classes) {
Class c = (Class)o;
//Make sure you identify only the packages that are holding references to the classloader.
//Allowing this code to clear all static references will result in all sorts
//of horrible things (like java segfaulting).
if (c.getName().startsWith("com.whatever")) {
//Kill any static references within all these classes.
for (Field f : c.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())
&& !Modifier.isFinal(f.getModifiers())
&& !f.getType().isPrimitive()) {
try {
f.setAccessible(true);
f.set(null, null);
} catch (Exception exception) {
//Log the exception
}
}
}
}
}
classes.clear();
该java.lang.OutOfMemoryError: PermGen
空间信息表明持久代的内存区域被耗尽。
允许任何Java应用程序使用有限数量的内存。在应用程序启动期间,将指定特定应用程序可以使用的确切内存量。
Java内存分为不同的区域,如下图所示:
元空间:新的内存空间诞生了
现在,JDK 8 HotSpot JVM使用本机内存来表示类元数据,这称为元空间。与Oracle JRockit和IBM JVM类似。
好消息是,这意味着不再有java.lang.OutOfMemoryError: PermGen
空间问题,也不再需要使用Java_8_Download或更高版本来调整和监视此内存空间。
可以做的第一件事是增大永久代堆空间的大小。这不能通过通常的–Xms(设置初始堆大小)和–Xmx(设置最大堆大小)JVM参数来完成,因为如上所述,永久生成堆空间与常规Java Heap空间完全分开,并且设置了这些参数此常规Java堆空间的空间。但是,可以使用类似的参数(至少与Sun / OpenJDK jvms一起使用)来增大永久代堆的大小:
-XX:MaxPermSize=128m
默认值为64m。
永久解决此问题的另一种方法是允许卸载类,以便您的PermGen永远不会用完:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
这样的东西过去对我来说是神奇的。但有一件事,在使用这些性能时需要进行重大的性能折衷,因为permgen扫描将为您提出的每个请求或类似的请求额外产生2个请求。您需要权衡利弊。
您可以找到此错误的详细信息。
http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html
另外,您可以切换到JRockit,它对permgen的处理方式与sun的jvm不同。通常它也具有更好的性能。
http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html
java.lang.OutOfMemoryError: There is insufficient native memory
。
这些天最简单的答案是使用Java 8。
它不再专门为PermGen空间保留内存,从而允许PermGen内存与常规内存池混合。
请记住,-XXPermGen...=...
如果您不希望Java 8抱怨它们没有执行任何操作,则必须删除所有非标准JVM启动参数。
由于使用大空间而不是jvm提供的空间来执行代码,因此会发生Perm gen空间错误。
在UNIX操作系统中,此问题的最佳解决方案是更改bash文件上的某些配置。以下步骤解决了该问题。
gedit .bashrc
在终端上运行命令。
创建JAVA_OTPS
具有以下值的变量:
export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"
保存bash文件。在终端上运行命令exec bash。重新启动服务器。
我希望这种方法可以解决您的问题。如果您使用的Java版本低于8,则有时会发生此问题。但是,如果您使用Java 8,则永远不会发生此问题。
我将Hibernate + Eclipse RCP组合在一起,尝试使用-XX:MaxPermSize=512m
和-XX:PermSize=512m
,它似乎对我有用。
我尝试了几个答案,最后唯一要做的就是对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>
希望这对您有所帮助。
内存的配置取决于应用程序的性质。
你在做什么?
进行多少笔交易?
您正在加载多少数据?
等等
等等
等等
可能您可以分析应用程序并开始清除应用程序中的某些模块。
显然,这可能是在重新部署应用程序几次之后发生的
Tomcat具有热部署,但占用内存。尝试不时重新启动容器。另外,您将需要知道在生产模式下运行所需的内存量,这对于该研究而言似乎是个好时机。
我遇到了完全相同的问题,但是不幸的是,没有一个建议的解决方案真正对我有用。在部署期间没有发生此问题,我也没有进行任何热部署。
以我为例,该问题在我的Web应用程序执行期间的同一时间每次都发生,同时(通过休眠)连接到数据库。
此链接(前面也提到过)确实提供了足够的内部解决问题的方法。将jdbc-(mysql)-驱动程序移出WEB-INF并移至jre / lib / ext /文件夹似乎已经解决了该问题。这不是理想的解决方案,因为升级到较新的JRE将需要您重新安装驱动程序。另一个可能引起类似问题的候选对象是log4j,因此您可能也希望将其移动
在这种情况下,第一步是检查是否允许GC从PermGen卸载类。在这方面,标准的JVM相当保守-类是天生的。因此,一旦加载,即使没有代码在使用它们,类也会保留在内存中。当应用程序动态创建许多类并且长时间不需要生成的类时,这可能会成为问题。在这种情况下,允许JVM卸载类定义可能会有所帮助。这可以通过在启动脚本中仅添加一个配置参数来实现:
-XX:+CMSClassUnloadingEnabled
默认情况下,此选项设置为false,因此要启用此功能,您需要在Java选项中显式设置以下选项。如果启用CMSClassUnloadingEnabled,GC也会清除PermGen并删除不再使用的类。请记住,只有同时使用以下选项启用UseConcMarkSweepGC时,此选项才起作用。因此,在运行ParallelGC或上帝禁止使用串行GC时,请确保通过指定以下内容将GC设置为CMS:
-XX:+UseConcMarkSweepGC
为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>
如果您在eclipse IDE中得到了此消息,即使在设置了参数
--launcher.XXMaxPermSize
,-XX:MaxPermSize
等之后,仍然遇到相同的错误,则很可能是eclipse使用的是错误版本的JRE,该版本已由某些人安装第三方应用程序并设置为默认。这些错误的版本不会使用PermSize参数,因此,无论您进行什么设置,您仍然会不断遇到这些内存错误。因此,在您的eclipse.ini中添加以下参数:
-vm <path to the right JRE directory>/<name of javaw executable>
另外,还要确保在Eclipse的首选项中将默认JRE设置为Java的正确版本。
我有类似的问题。我的是JDK 7 + Maven 3.0.2 + Struts 2.0 + Google GUICE依赖注入项目。
每当我尝试运行mvn clean package
命令时,它都会显示以下错误,并且发生“ BUILD FAILURE”
org.apache.maven.surefire.util.SurefireReflectionException:java.lang.reflect.InvocationTargetException; 嵌套的异常是java.lang.reflect.InvocationTargetException:空java.lang.reflect.InvocationTargetException原因:java.lang.OutOfMemoryError:PermGen空间
我尝试了上述所有有用的提示和技巧,但不幸的是没有一个对我有用。下面逐步介绍了对我有用的方法:=>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
元素,然后添加<argLine>
通过其中的子元素,-Xmx512m -XX:MaxPermSize=256m
如下所示=><configuration>
<argLine>-Xmx512m -XX:MaxPermSize=256m</argLine>
</configuration>
希望对您有所帮助,编程愉快:)