java - 在运行时加载 JRuby 和 ClassLoader 泄漏

标签 java ruby memory-leaks jruby classloader

我正在尝试在运行时动态加载 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/

相关文章:

java - 如何通过 ID 获取其他用户信息(用户名、名字)? [ key 斗篷]

java - 如何用 java 读取/写入 NetBeans 项目本身中的文件

ruby-on-rails - 基于 Ruby Enumerator 的惰性展平方法

Ruby 使用 Rspec stub Dir.glob

Java3D离屏渲染内存泄漏

java - TestNG父类的一些方法没有运行

java - 尝试从 .xsd 文件创建 Java 类时出错

ruby-on-rails - 对象数组比字符串数组慢得多

javascript - 使用数据 URI 快速更新图像会导致缓存、内存泄漏

iPhone 的图片泄露了,但是哪里呢?