我运行一个Groovy/Grails系统,动态编译并加载用户定义的代码.这基本上是通过GroovyClassLoader
.
我看到动态类本身都加载和卸载就好了.我添加了
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
Run Code Online (Sandbox Code Playgroud)
旗帜,工作正常.当类加载器是GC时,动态类正在卸载.
但是我的堆内存泄漏,这是泄漏的来源:
现在,Launcher$AppClassLoader
有一个parallelLockMap
定义的属性java.lang.ClassLoader
.显然,这ConcurrentHashMap
用于在Java 7中保存并行类加载锁.查看源代码.
此映射中保留的条目具有String
键(锁定适用的类名称)和Object
值(锁定对象).有两种泄漏密钥:
"groovy.runtime.metaclass.MyDynamicallyLoadedClassNameMetaClass"
"MyDynamicallyLoadedClassNameBeanInfo"
Run Code Online (Sandbox Code Playgroud)
所以对我来说,看起来这些键是在加载和类parallelLockMap
时创建的,但是当卸载关联的动态加载的类(及其元类)时,它们不会被删除.现在,这已经深入到Groovy及其元类系统的内部,我的专业知识已经耗尽.*MetaClass
*BeanInfo
这最终会让你离开堆空间,尽管需要一段时间:
String newClass = "class CLASSNAME {}"
while (true) {
GroovyClassLoader gcl = new GroovyClassLoader()
Class clazz = gcl.parseClass(newClass.replace("CLASSNAME", "NewClass"+System.nanoTime()))
clazz.newInstance()
}
Run Code Online (Sandbox Code Playgroud)
确保使用上面的JVM标志运行它,以便用尽堆空间,而不是PermGen空间.再次,PermGen很好地收集垃圾,没有泄漏.
1)这是Groovy或Java 7中的错误吗?清洁这个是谁的责任parallelLockMap
?我应该提交问题报告吗?
2)有解决方法吗?我正在考虑使用一个自定义ClassLoader
,它不会首先尝试将类加载委托给它们的父级以用于这些MetaClass
和BeanInfo
类,从而阻止了对它的调用java.lang.ClassLoader#loadClass(..)
.不过,我不是Java/Groovy类加载方面的专家.
编辑:我在这里提交了一个Groovy JIRA …
我有一些(非Grails-artifact)类通过传递grailsApplication对象来访问服务层bean .但是,我无法对以这种方式实现的类进行单元测试.为什么bean不在主上下文中注册?
@TestMixin(GrailsUnitTestMixin)
class ExampleTests {
void setUp() {}
void tearDown() {}
void testSomething() {
defineBeans {
myService(MyService)
}
assert grailsApplication.mainContext.getBean("myService") != null
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码失败了:
org.springframework.beans.factory.NoSuchBeanDefinitionException:没有定义名为'myService'的bean
我想要做的是通过grailsApplication从普通的旧Java类访问服务.这有效,但不适用于单元测试环境.我应该采用不同的方式吗?
class POJO {
MyService myService;
public POJO(GrailsApplication grailsApplication) {
myService = (MyService) grailsApplication.getMainContext().getBean("myService");
}
}
Run Code Online (Sandbox Code Playgroud)