user1870954
user1870954

Reputation: 207

Java ClassLoader keeps creating more threads

The overall concept of my program involves loading a plugin from .class files, running it, shutting it down, manually updating the .class file, and finally turning it back on. Currently it

  1. Loads the .class file via URLClassLoader and begins execution of the main method.
  2. The main method spawns another thread (VIA ScheduledThreadPoolExecutor) which queries a service every regularly.
  3. Spawns a new thread to handle cleanup:
    1. Calls .shutdown() on ScheduledThreadPoolExecutor, and all threads die.
    2. Calls .close() on URLClassLoader and sets URLClassLoader variable to null.
  4. The cleanup thread then sleeps to allow manual replacement of .class files.
  5. Cleanup thread then starts process over again, loading new .class files and running them.

Everything during these steps works, and the new .class files work as expected.

The issue I'm running into begins when the plugin is started again. Each time it runs through the restart process, it spawns one extra instance of the plugin.

1st run: 1 running plugin
2nd run: 2 running plugins
3rd run: 3 running plugins
and so on

What I find strange is that it isn't spawning double the amount of plugins on each start, it's only spawning one additional. The extra piece of code must only be executed one additional time, rather than by each previous thread. Could each subsequent second call to startup(), which creates a new URLClassLoader (the old one is closed and nulled out), also start up all of the past URLClassLoaders somehow? Tried running this in debug mode, and it isn't tracking every active thread. I need to maintain the previous URLClassLoader, without it any reference to existing objects running in the background are removed.

Hard to give SSCCE given the complexity of the program.

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   
    }
}    

Edit: All plugins are running the same lines of code at the same time. I mentioned those sleeps, which I suspect would throw off the different threads from being completely synchronized.

Upvotes: 4

Views: 271

Answers (1)

user1870954
user1870954

Reputation: 207

Mark W's suggestion lead me down a rabbit hole of using jmap and other process analysis programs to find out what exactly was going on. There's a ton of useful utilities out there. With the combination of VisualVM and Eclipse Memory Analyzer (MAT) I was able to figure out that I wasn't closing the FileHandler for my log. So many hours down the drain!

Upvotes: 1

Related Questions