Index: src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java
===================================================================
--- src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java	(revision 15480)
+++ src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java	(working copy)
@@ -55,6 +55,7 @@
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.FilterField;
 import org.openstreetmap.josm.plugins.PluginDownloadTask;
+import org.openstreetmap.josm.plugins.PluginHandler;
 import org.openstreetmap.josm.plugins.PluginInformation;
 import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask;
 import org.openstreetmap.josm.plugins.ReadRemotePluginInformationTask;
@@ -324,8 +325,12 @@
             List<String> l = new LinkedList<>(model.getSelectedPluginNames());
             Collections.sort(l);
             Config.getPref().putList("plugins", l);
-            if (!model.getNewlyDeactivatedPlugins().isEmpty())
-                return true;
+            List<PluginInformation> deactivatedPlugins = model.getNewlyDeactivatedPlugins();
+            if (!deactivatedPlugins.isEmpty()) {
+                boolean requiresRestart = PluginHandler.removePlugins(deactivatedPlugins);
+                if (requiresRestart)
+                    return requiresRestart;
+            }
             for (PluginInformation pi : model.getNewlyActivatedPlugins()) {
                 if (!pi.canloadatruntime)
                     return true;
Index: src/org/openstreetmap/josm/plugins/PluginHandler.java
===================================================================
--- src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 15480)
+++ src/org/openstreetmap/josm/plugins/PluginHandler.java	(working copy)
@@ -68,6 +68,7 @@
 import org.openstreetmap.josm.io.OfflineAccessException;
 import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.I18n;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -1167,7 +1168,6 @@
                         pluginsToDownload,
                         tr("Update plugins")
                 );
-
                 try {
                     pluginDownloadTask.run();
                 } catch (RuntimeException e) { // NOPMD
@@ -1331,6 +1331,18 @@
                     URL oldPluginURL = updatedPlugin.toURI().toURL();
                     pluginsToLoad.stream().filter(x -> x.libraries.contains(oldPluginURL)).forEach(
                             x -> Collections.replaceAll(x.libraries, oldPluginURL, newPluginURL));
+
+                    // Attempt to update loaded plugin (must implement Destroyable)
+                    PluginInformation tInfo = pluginsToLoad.parallelStream()
+                            .filter(x -> x.libraries.contains(newPluginURL)).findAny().orElse(null);
+                    if (tInfo != null) {
+                        Object tUpdatedPlugin = getPlugin(tInfo.name);
+                        if (tUpdatedPlugin instanceof Destroyable) {
+                            ((Destroyable) tUpdatedPlugin).destroy();
+                            PluginHandler.loadPlugins(getInfoPanel(), Collections.singleton(tInfo),
+                                    NullProgressMonitor.INSTANCE);
+                        }
+                    }
                 } catch (MalformedURLException e) {
                     Logging.warn(e);
                 }
@@ -1642,4 +1654,24 @@
             return cbDontShowAgain.isSelected();
         }
     }
+
+    /**
+     * Remove deactivated plugins, returning true if JOSM should restart
+     *
+     * @param deactivatedPlugins
+     * @return true if there was a plugin that requires a restart
+     */
+    public static boolean removePlugins(List<PluginInformation> deactivatedPlugins) {
+        List<Destroyable> noRestart = deactivatedPlugins.parallelStream()
+                .map(info -> PluginHandler.getPlugin(info.name)).filter(Destroyable.class::isInstance)
+                .map(Destroyable.class::cast).collect(Collectors.toList());
+        boolean returnBoolean;
+        try {
+            noRestart.forEach(Destroyable::destroy);
+            returnBoolean = deactivatedPlugins.size() == noRestart.size();
+        } catch (Exception e) {
+            returnBoolean = true;
+        }
+        return returnBoolean;
+    }
 }
