Java类加载器不断创建更多线程

Java类加载器不断创建更多线程,java,multithreading,Java,Multithreading,我的程序的总体概念包括从.class文件加载插件,运行插件,关闭插件,手动更新.class文件,最后将插件重新打开。现在是 通过URLClassLoader加载.class文件,并开始执行main方法 main方法生成另一个线程(通过ScheduledThreadPoolExecutor),该线程定期查询服务 生成新线程以处理清理: 调用ScheduledThreadPoolExecutor上的.shutdown(),所有线程都会死亡 调用URLClassLoader上的.close(),并将U

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

  • 通过
    URLClassLoader
    加载.class文件,并开始执行main方法
  • main方法生成另一个线程(通过ScheduledThreadPoolExecutor),该线程定期查询服务
  • 生成新线程以处理清理:
  • 调用
    ScheduledThreadPoolExecutor
    上的
    .shutdown()
    ,所有线程都会死亡
  • 调用
    URLClassLoader
    上的
    .close()
    ,并将
    URLClassLoader
    变量设置为null
  • 清理线程然后休眠以允许手动替换.class文件
  • 清理线程然后重新启动进程,加载新的.class文件并运行它们 在这些步骤中,一切都正常工作,新的.class文件也按预期工作

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

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

    我觉得奇怪的是,它不是在每次启动时生成两倍数量的插件,它只是生成一个额外的插件。额外的代码段只能执行一次,而不是由前一个线程执行。对
    startup()
    的后续第二次调用会创建一个新的
    URLClassLoader
    (旧的已关闭并为空),是否也会以某种方式启动所有过去的
    URLClassLoader
    ?尝试在调试模式下运行此程序,但它不会跟踪每个活动线程。我需要维护以前的
    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   
        }
    }    
    
    公共类插件处理器
    {
    私有静态URLClassLoader cl=null;
    私有静态字符串=“somedir”;
    公共无效重新启动()
    { 
    (新线程(){
    公开募捐{
    如果(cl!=null){
    cl.invokeClass(pckg,“主”,“关闭”);
    cl.关闭();
    cl=零;
    }
    试一试{
    睡眠(15000);
    }(捕捉中断异常e){
    系统输出打印项次(“中断”);
    }
    cl=URLClassLoader cl=newurlclassloader(新URL[]{new File(path).toURI().toURL()}));
    cl.invokeClass(pckg,“主”,“启动”);
    }).start();
    }
    公共URLClassLoader invokeClass(字符串pckgName、字符串mthdName、字符串[]args)
    抛出异常
    {
    类loadedClass=cl.loadClass(pckgName);
    方法m=loadedClass.getMethod(mthdName,args.getClass());
    m、 调用(null,新对象[]{args});
    返回urlcl;
    }
    }
    公共类插件
    { 
    公共静态void main(字符串[]args){
    if(args[0]。等于(“-startup”){
    新的pluginControl.run();
    }
    else if(args[0]。等于(“-shutdown”){
    pluginControl.shutdown();
    }
    }
    }
    公共类插件InControl实现可运行
    {
    静态ScheduledThreadPoolExecutor st;
    静态调度未来sf;
    公开募捐{
    st=新的计划线程池执行器(1);
    sf=st.scheduleWithFixedDelay(新插件(),0,10,时间单位为秒);
    sf.wait();
    System.out.println(“从运行()返回”);//运行关机后打印。
    }
    公共静态无效关机(){
    sf.cancel();
    st.shutdown();
    }
    }
    公共类插件实现可运行
    {
    公开募捐{
    //询问一些服务
    }
    }    
    

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

    Mark W的建议让我陷入了一个兔子洞,无法使用jmap和其他流程分析程序来找出到底发生了什么。有很多问题有用的实用程序。通过和的组合,我能够发现我没有关闭日志的文件处理程序。这么多小时都在浪费!

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