15

我在运行 ~300 个 JUnit 测试并使用 Spring 上下文时看到“java.lang.OutOfMemoryError: PermGen space”。很难弄清楚是什么吞噬了 PermGen,因为:

  • 在稳定状态下,应用程序消耗大约 90m 的 permgen 空间
  • 我已经尝试过 -XX:MaxPermSize=256m 进行单元测试 - 仍然用完
  • -XX:+TraceClassLoading启用和-XX:+TraceClassUnloading启用后,在OutOfMemoryError.

后者似乎表明除了 Class 对象之外的东西正在填充 PermGen,不是吗?如果是这样,那会是什么?例如,是否存在类实例存储在 PermGen 中的情况?

这是我的虚拟机信息:

$ java -version
java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)

有关的

FWIW,导致这篇文章的问题的根源结果有点微不足道:我假设 Maven Surefire 插件在分叉 VM 时从 MAVEN_OPTS(或运行 mvn 的 VM 实例)继承 VM 设置 -它没有(嘘)。必须在插件的配置中明确指定使用 argLine 的那些。HTH。

4

3 回答 3

4

有时滥用String.intern()会导致 PermGen 空间不足错误,因为实习字符串实例存储在 PermGen 中。

这可能就是您所看到的 - 尝试消除不必要的 String.intern() 调用,看看这是否解决了问题。一般来说,我不建议使用 String.intern() ,除非您确定以下两个都是正确的:

  • 您确定只会添加有限数量的字符串
  • 您实际上需要这样的字符串来共享相同的对象身份(例如,如果相同字符串的许多实例会消耗不可接受的内存量,或者出于复杂的性能原因您需要依赖 == 进行字符串比较)
于 2011-06-14T17:37:23.960 回答
2

实习字符串也存储在 permgen 中,尽管数百兆字节的字符串似乎不太可能。请记住,您使用代理的每个 Spring Bean 都会在运行时动态生成新类,以实现您代理的接口。(或者你的类,如果你使用 CGLIB 代理等。)所以如果你为每个 JUnit 创建一个“新的”Spring ApplicationContext,你实际上是在创建 300 个所有代理的副本等。

还要记住,每个类加载器的 Class 实例都是唯一的,而不是在整个 permgen 空间中,因此实际上可能存在重复,具体取决于您的运行设置方式(如果它涉及在容器中部署或其他东西,尽管这在JUnit :) )。

于 2011-06-14T17:40:33.337 回答
1

我注意到您正在运行 64 位 JVM。众所周知,它们使用机器上实际内存的两倍,因为每次分配需要两倍大的内存空间。

如果您的 JUnit 测试实际上加载了一个 spring 上下文(毕竟不是 Unit),这些将实例化您的 appContext 中的所有 bean。这很可能需要超过 128mb(64 位机器上为 256mb)的内存。

根据我的经验,为 64 位机器上的大型测试套件分配半个或更多的演出并不荒谬。尝试将其提高到 512mb 甚至 1gb。

这些是我运行较大项目的测试套件之一的选项...

-Xms256m
-Xmx512m
-XX:MaxPermSize=512m
于 2011-06-14T17:44:00.043 回答