问题描述
|
我在运行〜300个JUnit测试并使用Spring上下文时看到\'java.lang.OutOfMemoryError:PermGen space \'。由于以下原因,很难弄清正在吞噬PermGen的原因:
在稳定状态下,该应用程序会消耗约90m的永久性空间
我已经尝试-XX:MaxPermSize = 256m进行单元测试-仍然用光
启用
-XX:+TraceClassLoading
和-XX:+TraceClassUnloading
时,在执行OutOfMemoryError
之前的最后20-30个测试时,我看不到其他“加载”事件。
后者似乎表明,除了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设置-不会(boo)。必须在插件的配置中使用argLine明确指定那些。 HTH。
解决方法
有时滥用String.intern()可能会导致PermGen空间错误,因为已嵌入的String实例存储在PermGen中。
这可能就是您正在看到的-尝试消除不必要的String.intern()调用以查看是否可以解决问题。通常,除非您确定以下两个条件都成立,否则我不建议您使用String.intern():
您确定只会添加有限数量的字符串
您实际上需要这样的字符串来共享相同的对象标识(例如,如果相同字符串的许多实例将消耗不可接受的内存量,或者出于复杂的性能原因,您需要依靠==进行字符串比较)
, 暂存的字符串也存储在permgen中,尽管不太可能出现数百兆字节的字符串。记住,您使用代理的每个Spring Bean都会在运行时动态生成新类,以实现您代理的接口。 (或者,如果您使用的是CGLIB代理,则是您的类,等等。)因此,如果您为每个JUnit创建一个“新的” Spring ApplicationContext,那么实际上您将获得所有代理的300个副本,等等。
还要记住,每个类加载器的Class实例是唯一的,而不是整个permgen空间中的实例,因此实际上可能存在重复项,具体取决于运行的设置方式(如果它涉及在容器中进行部署等),尽管在容器中似乎不太可能JUnit :))。
, 我注意到您正在运行64位JVM。已知它们使用计算机上实际内存的两倍,因为每个分配需要两倍的内存空间。
如果您具有实际加载spring上下文的JUnit测试(毕竟不是加载Unit),则这些测试将实例化appContext中的所有bean。这很可能需要超过128mb(在64位设备上为256mb)的内存。
以我的经验,在64位计算机上为大型测试套件分配半个或更多的演出并不荒谬。尝试将其提升到512mb甚至1gb。
这些是我使用...运行较大项目的测试套件之一的选项。
-Xms256m
-Xmx512m
-XX:MaxPermSize=512m