我正在尝试在运行时动态加载 JRuby(这样我就可以使用任意 JRuby 安装和版本执行 Ruby 代码)。我的计划大致是创建一个可以访问 jruby.jar 的 ClassLoader,然后使用它来加载必要的 JRuby 运行时等。一切都很顺利,直到我需要多次执行此操作。如果我销毁第一个 JRuby 运行时,第三个或第四个将导致 OutOfMemory: PermGen space。
我已将其简化为一个最小的示例。该示例同时使用“直接”API 以及 JRuby Embed API 。 “直接”API 部分已被注释掉,但两者表现出相同的行为:经过几次迭代后,PermGen 内存不足。 (使用 JRuby 1.6.7 和 JRuby 1.6.5.1 进行测试)
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import org.junit.Test;
public class JRubyInstantiationTeardownTest {
@Test
public void test() throws Exception {
for (int i = 0; i < 100; ++i) {
URL[] urls = new URL[] {
new URL("file://path/to/jruby-1.6.7.jar")
};
ClassLoader cl = new URLClassLoader(urls, this.getClass().getClassLoader());
// "Direct" API
/*
Class<?> klass = cl.loadClass("org.jruby.Ruby");
Method newInstance = klass.getMethod("newInstance");
Method evalScriptlet = klass.getMethod("evalScriptlet", String.class);
Method tearDown = klass.getMethod("tearDown");
Object runtime = newInstance.invoke(null);
System.out.println("have " + runtime);
evalScriptlet.invoke(runtime, "puts 'hello, world'");
tearDown.invoke(runtime);
*/
// JRuby Embed API
Class<?> scriptingContainerClass = cl.loadClass("org.jruby.embed.ScriptingContainer");
Method terminate = scriptingContainerClass.getMethod("terminate");
Method runScriptlet = scriptingContainerClass.getMethod("runScriptlet", String.class);
Object container = scriptingContainerClass.newInstance();
System.out.println("have " + container);
runScriptlet.invoke(container, "puts 'hello, world'");
terminate.invoke(container);
}
}
}
问题:使用类加载器尝试这样做合理吗?如果是这样,这是 JRuby 中的错误,还是我的类加载有问题?
额外奖励:如果这是 JRuby 中的错误,那么 Eclipse 内存分析工具之类的工具如何帮助找到源代码?我可以打开堆转储并看到几个 Ruby 对象(我希望在任何给定时间不会超过一个),但我不确定如何找到为什么这些对象没有被垃圾收集...
最佳答案
尝试查看 stackoverflow:loading classes with different classloaders to unload them from the JVM when not needed以及来自那里的引用文献。成熟的 Web 容器(如 Tomcat)的来源应该在加载/卸载堆栈中的某个位置提供您的问题的答案。
PermGen 存储加载的类(和生成的动态代理)的字节码。当清除所有对类及其类加载器的引用时,GC 应该正确压缩它。但您的代码证明,某些东西使您的 JRuby 类保持锁定并可从主类加载器访问。它可能是 JRuby 在加载时注册自身的某种回调映射。
关于java - 在运行时加载 JRuby 和 ClassLoader 泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9541207/