提问人: 提问时间:9/18/2008 最后编辑:8 revs, 6 users 55%Chris 更新时间:3/14/2022 访问量:1062602
处理“java.lang.OutOfMemoryError: PermGen space”错误
Dealing with "java.lang.OutOfMemoryError: PermGen space" error
问:
最近,我在Web应用程序中遇到了以下错误:
java.lang.OutOfMemoryError:PermGen 空间
这是一个典型的 Hibernate/JPA + IceFaces/JSF 应用程序,运行在 Tomcat 6 和 JDK 1.6 上。 显然,这可能会在重新部署应用程序几次后发生。
是什么原因导致的,可以做些什么来避免它? 如何解决该问题?
答:
解决方案是在启动 Tomcat 时将这些标志添加到 JVM 命令行:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
您可以通过关闭 tomcat 服务,然后进入 Tomcat/bin 目录并运行 tomcat6w.exe 来做到这一点。在“Java”选项卡下,将参数添加到“Java 选项”框中。单击“确定”,然后重新启动服务。
如果收到错误,指定的服务不存在为已安装的服务,则应运行:
tomcat6w //ES//servicename
其中 serviceName 是在 services.msc 中查看的服务器的名称
来源:orx 对 Eric 的 Agile Answers 的评论。
评论
或者,你可以切换到 JRockit,它处理 permgen 的方式与 sun 的 jvm 不同。它通常也具有更好的性能。
http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html
评论
java.lang.OutOfMemoryError: There is insufficient native memory
Jrockit 也为我解决了这个问题;但是,我注意到 servlet 重启时间要差得多,所以虽然它在生产中更好,但在开发中有点拖累。
对 Sun JVM 使用命令行参数(显然,将 128 替换为您需要的任何大小)。-XX:MaxPermSize=128m
评论
多次部署后发生的应用服务器 PermGen 错误很可能是由容器对旧应用的类加载器的引用引起的。例如,使用自定义日志级别类将导致引用由应用服务器的类加载器保存。您可以通过使用现代 (JDK6+) JVM 分析工具(如 jmap 和 jhat)来检测这些类加载器间泄漏,以查看哪些类继续保留在您的应用程序中,并重新设计或消除它们的使用。常见的可疑对象是数据库、记录器和其他基本框架级库。
参见 Classloader leaks: the dreaded “java.lang.OutOfMemoryError: PermGen space” exception,尤其是它的后续文章。
评论
尝试,如果它仍然存在,请尝试-XX:MaxPermSize=256m
-XX:MaxPermSize=512m
评论
XX:MaxPermSize=1024m
内存的配置取决于应用的性质。
你在干什么?
之前的交易金额是多少?
您加载了多少数据?
等。
等。
等
也许你可以分析你的应用,并开始清理你的应用中的一些模块。
显然,这可能会在重新部署应用程序几次后发生
Tomcat 具有热部署功能,但会消耗内存。尝试偶尔重新启动容器。此外,您还需要知道在生产模式下运行所需的内存量,这似乎是进行研究的好时机。
您最好尝试 -XX:MaxPermSize=128M
而不是 .-XX:MaxPermGen=128M
我无法说出这个内存池的确切用途,但它与加载到 JVM 中的类数量有关。(因此,启用 tomcat 的类卸载可以解决该问题。如果应用程序在运行时生成和编译类,则更有可能需要比默认值更大的内存池。
评论
我有一个 Hibernate+Eclipse RCP 的组合,尝试使用并且它似乎对我有用。-XX:MaxPermSize=512m
-XX:PermSize=512m
设置。稍后,您也可以尝试增加 .希望它能起作用。这同样适用于我。只有设置对我不起作用。-XX:PermSize=64m -XX:MaxPermSize=128m
MaxPermSize
MaxPermSize
他们说 Tomcat 的最新版本(6.0.28 或 6.0.29)可以更好地处理重新部署 servlet 的任务。
“他们”是错误的,因为我正在运行 6.0.29,即使在设置了所有选项后也遇到了同样的问题。正如蒂姆·豪兰德(Tim Howland)上面所说,这些选择只会推迟不可避免的事情。它们允许我在遇到错误之前重新部署 3 次,而不是每次重新部署。
如果您在eclipse IDE中得到此消息,即使在设置了参数,,等之后,仍然遇到相同的错误,则很可能是eclipse使用的JRE版本有问题,该版本将由某些第三方应用程序安装并设置为默认值。这些有问题的版本不会获取 PermSize 参数,因此无论您设置什么,您仍然会不断收到这些内存错误。因此,在您的eclipse.ini中添加以下参数:--launcher.XXMaxPermSize
-XX:MaxPermSize
-vm <path to the right JRE directory>/<name of javaw executable>
此外,请确保在 eclipse 的首选项中将默认 JRE 设置为正确的 java 版本。
唯一对我有用的方法是使用 JRockit JVM。我有MyEclipse 8.6。
JVM 的堆存储了正在运行的 Java 程序生成的所有对象。Java 使用运算符创建对象,并在运行时在堆上分配新对象的内存。垃圾回收是一种自动释放程序不再引用的对象所包含的内存的机制。new
我遇到了我们在这里讨论的问题,我的场景是eclipse-helios + tomcat + jsf,您正在做的是将一个简单的应用程序部署到tomcat。我在这里展示了同样的问题,解决方法如下。
在eclipse中,转到服务器选项卡,在我的情况下双击已注册的服务器tomcat 7.0,它会打开我的文件服务器常规注册信息。在“常规信息”部分,单击链接“打开启动配置”,这将在“参数”选项卡中打开服务器选项的执行,在最后添加这两个条目的VM参数
-XX: MaxPermSize = 512m
-XX: PermSize = 512m
并准备好了。
我遇到了完全相同的问题,但不幸的是,没有一个建议的解决方案真正适合我。这个问题在部署期间没有发生,我也没有做任何热部署。
就我而言,在执行我的 Web 应用程序期间,每次在连接(通过休眠)数据库时,问题都会发生。
这个链接(前面也提到过)确实提供了足够的内部信息来解决问题。将 jdbc-(mysql)-driver 移出 WEB-INF 并移入 jre/lib/ext/ 文件夹似乎已经解决了这个问题。这不是理想的解决方案,因为升级到较新的 JRE 需要重新安装驱动程序。 另一个可能导致类似问题的候选者是 log4j,因此您可能也希望移动该候选者
评论
此外,如果您在 Web 应用程序中使用 log4j,请查看 log4j 文档中的此段落。
似乎如果您正在使用 ,则在取消部署 Web 应用程序时会导致内存泄漏。PropertyConfigurator.configureAndWatch("log4j.properties")
在部署和取消部署复杂的 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();
评论
我尝试了几个答案,最终唯一完成这项工作的是 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>
希望这个有帮助。
评论
人们常犯的错误是认为堆空间和permgen空间是一样的,这根本不是真的。堆中可能还有很多剩余空间,但仍然可能在 permgen 中耗尽内存。
PermGen 中 OutofMemory 的常见原因是 ClassLoader。每当一个类被加载到 JVM 中时,它的所有元数据以及 Classloader 都保存在 PermGen 区域中,当加载它们的 Classloader 准备好进行垃圾回收时,它们将被垃圾回收。如果 Classloader 有内存泄漏,那么它加载的所有类都将保留在内存中,并在重复几次后导致 permGen 内存不足。典型的例子是 Tomcat 中的 Java.lang.OutOfMemoryError:PermGen Space。
现在有两种方法可以解决这个问题:
1.找到内存泄漏的原因或是否有任何内存泄漏。
2. 使用 JVM 参数和 .-XX:MaxPermSize
-XX:PermSize
您还可以查看 2 Java 中的 Java.lang.OutOfMemoryError 解决方案 了解更多详细信息。
评论
-XX:MaxPermSize and -XX:PermSize
catalina.bat
5.5.26
如果存在真正的内存泄漏,则增加永久生成大小或调整 GC 参数将无济于事。如果您的应用程序或它使用的某些第三方库泄漏了类加载器,那么唯一真正且永久的解决方案是找到此泄漏并修复它。有许多工具可以帮助您,最近的工具之一是 Plumbr,它刚刚发布了具有所需功能的新版本。
当我使用 eclipse ide 时,我向 VM 参数添加了(您可以尝试哪种效果最好)。在大多数 JVM 中,默认的 PermSize 约为 64MB,如果项目中有太多的类或大量的 String,内存就会耗尽。-XX: MaxPermSize = 128m
对于日食,在答案中也有描述。
步骤1:双击tomcat服务器的服务器选项卡
第 2 步:打开启动 Conf 并添加到现有 VM 参数的末尾。-XX: MaxPermSize = 128m
评论
- 从 Tomcat 的 bin 目录中打开 tomcat7w,或在开始菜单中键入 Monitor Tomcat (将打开一个选项卡式窗口,其中包含各种服务信息)。
在 Java 选项文本区域中,附加以下行:
-XX:MaxPermSize=128m
- 将“初始内存池”设置为“1024”(可选)。
- 将“最大内存池”设置为 1024(可选)。
- 单击“确定”。
- 重新启动 Tomcat 服务。
现在最简单的答案是使用 Java 8。
它不再专门为 PermGen 空间保留内存,允许 PermGen 内存与常规内存池混合。
请记住,如果您不希望 Java 8 抱怨它们什么都不做,就必须删除所有非标准的 JVM 启动参数。-XXPermGen...=...
评论
1) 增加 PermGen 内存大小
首先要做的就是把永久生成堆空间的大小变大。这不能用通常的 –Xms(set initial heap size) 和 –Xmx(set maximum heap size) JVM 参数来完成,因为如前所述,永久生成堆空间与常规 Java 堆空间完全分开, 这些参数为这个常规的 Java 堆空间设置了空间。但是,可以使用类似的参数(至少在 Sun/OpenJDK jvms 中)来使永久生成堆的大小更大:
-XX:MaxPermSize=128m
默认值为 64m。
2) 启用扫描
另一种永久处理此问题的方法是允许卸载类,这样您的 PermGen 就不会用完:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
过去,像这样的东西对我有用。不过,有一件事是,使用这些会有很大的性能权衡,因为 permgen 扫描会为您提出的每个请求或类似的东西发出额外的 2 个请求。您需要在使用与权衡之间取得平衡。
您可以找到此错误的详细信息。
http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html
评论
Perm 生成空间错误是由于使用了大空间而不是 jvm 提供的空间来执行代码而发生的。
在 UNIX 操作系统中,此问题的最佳解决方案是更改 bash 文件上的某些配置。以下步骤解决了该问题。
在终端上运行命令。gedit .bashrc
使用以下值创建变量:JAVA_OTPS
export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"
保存 bash 文件。在终端上运行命令 exec bash。重新启动服务器。
我希望这种方法能解决你的问题。如果您使用低于 8 的 Java 版本,有时会出现此问题。但是,如果您使用 Java 8,则永远不会发生此问题。
我也有类似的问题。 我的是 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: null java.lang.reflect.InvocationTargetException 原因:java.lang.OutOfMemoryError:PermGen 空间
我尝试了上述所有有用的提示和技巧,但不幸的是,没有一个对我有用。 对我有用的东西在下面逐步描述:=>
- 前往您的pom.xml
- 寻找
<artifactId>maven-surefire-plugin</artifactId>
- 添加一个新元素,然后添加子元素,如下所示 =>
<configuration>
<argLine>
-Xmx512m -XX:MaxPermSize=256m
<configuration>
<argLine>-Xmx512m -XX:MaxPermSize=256m</argLine>
</configuration>
希望对您有所帮助,祝您编程愉快:)
在这种情况下,第一步是检查是否允许 GC 从 PermGen 卸载类。标准 JVM 在这方面是相当保守的——类生来就是为了永生。因此,一旦加载,即使没有代码再使用它们,类也会保留在内存中。当应用程序动态创建大量类并且长时间不需要生成的类时,这可能会成为一个问题。在这种情况下,允许 JVM 卸载类定义可能会有所帮助。这可以通过向启动脚本添加一个配置参数来实现:
-XX:+CMSClassUnloadingEnabled
默认情况下,这设置为 false,因此要启用它,您需要在 Java 选项中显式设置以下选项。如果启用 CMSClassUnloadingEnabled,GC 也会扫描 PermGen 并删除不再使用的类。请记住,仅当使用以下选项启用 UseConcMarkSweepGC 时,此选项才有效。因此,在运行 ParallelGC 或 Serial GC 时,请确保通过指定以下命令将 GC 设置为 CMS:
-XX:+UseConcMarkSweepGC
空格消息表示永久生成在内存中的区域已耗尽。java.lang.OutOfMemoryError: PermGen
允许任何 Java 应用程序使用有限数量的内存。特定应用程序可以使用的确切内存量是在应用程序启动期间指定的。
Java 内存分为不同的区域,如下图所示:
元空间:一个新的内存空间诞生了
JDK 8 HotSpot JVM 现在使用本机内存来表示类元数据,称为元空间;类似于 Oracle JRockit 和 IBM JVM。
好消息是,这意味着不再有 java.lang.OutOfMemoryError: PermGen
空间问题,并且您不再需要使用 Java_8_Download 或更高版本来调整和监控此内存空间。
评论
MetaSpace
也可以去 OutOfMemory
分配 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>
评论