【问题标题】:Java ClassLoader keeps creating more threadsJava ClassLoader 不断创建更多线程
【发布时间】:2014-07-17 14:35:17
【问题描述】:

我的程序的总体概念包括从 .class 文件加载插件、运行它、关闭它、手动更新 .class 文件,最后重新打开它。目前它

  1. 通过URLClassLoader 加载.class 文件并开始执行main 方法。
  2. main 方法生成另一个线程 (VIA ScheduledThreadPoolExecutor),它会定期查询服务。
  3. 产生一个新线程来处理清理:
    1. ScheduledThreadPoolExecutor 上调用.shutdown(),所有线程都死掉了。
    2. URLClassLoader 上调用.close() 并将URLClassLoader 变量设置为null。
  4. 然后清理线程休眠以允许手动替换 .class 文件。
  5. 清理线程然后重新启动进程,加载新的 .class 文件并运行它们。

在这些步骤中一切正常,新的 .class 文件按预期工作。

当插件再次启动时,我遇到的问题就开始了。每次运行重新启动过程时,它都会产生一个额外的插件实例。

第一次运行:1 个正在运行的插件
第二次运行:2 个正在运行的插件
第 3 次运行:3 个正在运行的插件
等等

我觉得奇怪的是,每次启动时它不会产生两倍的插件数量,它只会产生一个额外的插件。额外的代码只能再执行一次,而不是由之前的每个线程执行。每次对startup() 的第二次调用会创建一个新的URLClassLoader(旧的已关闭并无效),是否也会以某种方式启动所有过去的URLClassLoaders?尝试在调试模式下运行它,它并没有跟踪每个活动线程。我需要维护以前的URLClassLoader,没有它对在后台运行的现有对象的任何引用都将被删除。

鉴于程序的复杂性,很难给出 SSCCE。

public class PluginHandler 
{
    private static URLClassLoader cl = null;
    private static String = "somedir";

    public void restart() 
    { 
        (new Thread() {
            public void run() {
                if (cl != null) {
                    cl.invokeClass(pckg, "main", "-shutdown");
                    cl.close();
                    cl = null;
                }
                try {
                    Thread.sleep(15000); 
                 } (catch InterruptedException e) {
                     System.out.println("Interrupted");
                 }
                 cl = URLClassLoader cl = new URLClassLoader(new URL[] { new File(path).toURI().toURL() } ));
                 cl.invokeClass(pckg, "main", "-startup");                
     }).start();
}

public URLClassLoader invokeClass(String pckgName, String mthdName, String[] args)
                throws Exception
    {
      Class<?> loadedClass = cl.loadClass(pckgName);
      Method m = loadedClass.getMethod(mthdName, args.getClass());
      m.invoke(null, new Object[] { args });
      return urlcl;
    }
}

public class PluginMain 
{ 
     public static void main(String[] args) {
         if (args[0].equals("-startup") {
             new PluginController.run();
         }
         else if (args[0].equals("-shutdown") {
             PluginController.shutdown();
         }
     }
}

public class PluginController implements Runnable
{
    static ScheduledThreadPoolExecutor st;
    static ScheduledFuture<?> sf;

    public void run() {
        st = new Scheduled ThreadPoolExecutor(1);
        sf = st.scheduleWithFixedDelay(new Plugin(), 0, 10, Time_Unit.SECONDS);
        sf.wait();
        System.out.println("Returns from run()"); //prints after shutdown is run.
    }

    public static void shutdown() {
        sf.cancel();
        st.shutdown();
    }
}

public class Plugin implements Runnable
{
    public void run() {
        //Queries some service   
    }
}    

编辑:所有插件同时运行相同的代码行。我提到了那些睡眠,我怀疑这会使不同的线程无法完全同步。

【问题讨论】:

  • 也许这与元/永久代空间有关。类加载器加载的插件的第一个实例继续运行,因为它永远不符合 GC 条件?与后续加载相同。
  • 哦,我没有提到。所有插件同时运行相同的代码行。我提到了那些睡眠,我怀疑这会使不同的线程无法完全同步。

标签: java multithreading


【解决方案1】:

Mark W 的建议使我陷入了使用 jmap 和其他过程分析程序来找出究竟发生了什么的兔子洞。那里有很多有用的实用程序。结合VisualVMEclipse Memory Analyzer (MAT) 我发现我没有为我的日志关闭 FileHandler。 浪费了这么多小时!

【讨论】:

    猜你喜欢
    • 2019-05-01
    • 2011-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    • 1970-01-01
    • 2016-03-11
    相关资源
    最近更新 更多