From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 1 May 2018 21:33:35 -0400
Subject: [PATCH] Close Plugin Class Loaders on Disable

This should close more memory leaks from /reload and disabling plugins,
by closing the class loader and the jar file.

diff --git a/src/main/java/org/bukkit/plugin/PluginLoader.java b/src/main/java/org/bukkit/plugin/PluginLoader.java
index a88733f1cd1ddb5d85ab1b0e6af4fd5b80bbc1c6..6ab9cd8213cbe35943748dcf42948d5fc048c84c 100644
--- a/src/main/java/org/bukkit/plugin/PluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/PluginLoader.java
@@ -77,4 +77,18 @@ public interface PluginLoader {
      * @param plugin Plugin to disable
      */
     public void disablePlugin(@NotNull Plugin plugin);
+    // Paper start - close Classloader on disable
+    /**
+     * Disables the specified plugin
+     * <p>
+     * Attempting to disable a plugin that is not enabled will have no effect
+     *
+     * @param plugin Plugin to disable
+     * @param closeClassloader if the classloader for the Plugin should be closed
+     */
+    // provide default to allow other PluginLoader implementations to work
+    default public void disablePlugin(@NotNull Plugin plugin, boolean closeClassloader) {
+        disablePlugin(plugin);
+    }
+    // Paper end - close Classloader on disable
 }
diff --git a/src/main/java/org/bukkit/plugin/PluginManager.java b/src/main/java/org/bukkit/plugin/PluginManager.java
index 41e26451fe12d8e6e0ef73c85731b24b4e3f200c..86cc5025ad98f7a752c51713b7cd6a39d5136ecc 100644
--- a/src/main/java/org/bukkit/plugin/PluginManager.java
+++ b/src/main/java/org/bukkit/plugin/PluginManager.java
@@ -161,6 +161,18 @@ public interface PluginManager {
      */
     public void disablePlugin(@NotNull Plugin plugin);
 
+    // Paper start - close Classloader on disable
+    /**
+     * Disables the specified plugin
+     * <p>
+     * Attempting to disable a plugin that is not enabled will have no effect
+     *
+     * @param plugin Plugin to disable
+     * @param closeClassloader if the classloader for the Plugin should be closed
+     */
+    public void disablePlugin(@NotNull Plugin plugin, boolean closeClassloader);
+    // Paper end - close Classloader on disable
+
     /**
      * Gets a {@link Permission} from its fully qualified name
      *
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index 8b33d914d29897c0276f9e2e7ce83bd2c316d5e2..a7393d2830b95d7167121b02066a3f357cee6085 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -492,17 +492,28 @@ public final class SimplePluginManager implements PluginManager {
 
     @Override
     public void disablePlugins() {
+        disablePlugins(false);
+    }
+
+    public void disablePlugins(boolean closeClassloaders) {
+        // Paper end - close Classloader on disable
         Plugin[] plugins = getPlugins();
         for (int i = plugins.length - 1; i >= 0; i--) {
-            disablePlugin(plugins[i]);
+            disablePlugin(plugins[i], closeClassloaders); // Paper - close Classloader on disable
         }
     }
 
     @Override
     public void disablePlugin(@NotNull final Plugin plugin) {
+        disablePlugin(plugin, false);
+    }
+
+    @Override
+    public void disablePlugin(@NotNull final Plugin plugin, boolean closeClassloader) {
+        // Paper end - close Classloader on disable
         if (plugin.isEnabled()) {
             try {
-                plugin.getPluginLoader().disablePlugin(plugin);
+                plugin.getPluginLoader().disablePlugin(plugin, closeClassloader); // Paper - close Classloader on disable
             } catch (Throwable ex) {
                 handlePluginException("Error occurred (in the plugin loader) while disabling "
                         + plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
@@ -557,7 +568,7 @@ public final class SimplePluginManager implements PluginManager {
     @Override
     public void clearPlugins() {
         synchronized (this) {
-            disablePlugins();
+            disablePlugins(true); // Paper - close Classloader on disable
             plugins.clear();
             lookupNames.clear();
             dependencyGraph = GraphBuilder.directed().build();
diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index 5be6460e8eb81381c7e305cb7ab6b77c0c7a8fe5..bef88a6e2e6f7071401a3af0aec31e62aa265566 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -331,7 +331,7 @@ public final class JavaPluginLoader implements PluginLoader {
             } catch (Throwable ex) {
                 server.getLogger().log(Level.SEVERE, "Error occurred while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
                 // Paper start - Disable plugins that fail to load
-                disablePlugin(jPlugin);
+                server.getPluginManager().disablePlugin(jPlugin, true); // Paper - close Classloader on disable - She's dead jim
                 return;
                 // Paper end
             }
@@ -344,6 +344,12 @@ public final class JavaPluginLoader implements PluginLoader {
 
     @Override
     public void disablePlugin(@NotNull Plugin plugin) {
+    // Paper start - close Classloader on disable
+        disablePlugin(plugin, false); // Retain old behavior unless requested
+    }
+
+    public void disablePlugin(@NotNull Plugin plugin, boolean closeClassloader) {
+        // Paper end - close Class Loader on disable
         Validate.isTrue(plugin instanceof JavaPlugin, "Plugin is not associated with this PluginLoader");
 
         if (plugin.isEnabled()) {
@@ -370,6 +376,16 @@ public final class JavaPluginLoader implements PluginLoader {
                 for (String name : names) {
                     removeClass(name);
                 }
+                // Paper start - close Class Loader on disable
+                try {
+                    if (closeClassloader) {
+                        loader.close();
+                    }
+                } catch (IOException e) {
+                    server.getLogger().log(Level.WARNING, "Error closing the Plugin Class Loader for " + plugin.getDescription().getFullName());
+                    e.printStackTrace();
+                }
+                // Paper end
             }
         }
     }
