diff --git a/addons/source-python/packages/source-python/core/__init__.py b/addons/source-python/packages/source-python/core/__init__.py
index b0f548dd7..c05d97dff 100644
--- a/addons/source-python/packages/source-python/core/__init__.py
+++ b/addons/source-python/packages/source-python/core/__init__.py
@@ -19,6 +19,8 @@
 from platform import system
 #   Sys
 import sys
+#   Weakref
+from weakref import WeakValueDictionary
 
 # Site-Packages Imports
 #   ConfigObj
@@ -44,6 +46,7 @@
 # >> ALL DECLARATION
 # =============================================================================
 __all__ = ('AutoUnload',
+           'WeakAutoUnload',
            'GAME_NAME',
            'PLATFORM',
            'SOURCE_ENGINE',
@@ -64,9 +67,6 @@
 # Get the sp.core logger
 core_logger = _sp_logger.core
 
-# Create a dictionary to store AutoUnload object in
-_module_instances = defaultdict(list)
-
 
 # =============================================================================
 # >> CLASSES
@@ -77,6 +77,9 @@ class AutoUnload(object):
     Each inheriting class must implement an _unload_instance method.
     """
 
+    # Create a dictionary to store AutoUnload objects in
+    _module_instances = defaultdict(list)
+
     def __new__(cls, *args, **kwargs):
         """Overwrite __new__ to store the calling module."""
         # Get the class instance
@@ -85,12 +88,16 @@ def __new__(cls, *args, **kwargs):
         # Get the calling module
         caller = getmodule(stack()[1][0])
 
-        # Set the _calling_module attribute for the instance
-        _module_instances[caller.__name__].append(self)
+        # Call class-specific logic for adding the instance.
+        self._add_instance(caller.__name__)
 
         # Return the instance
         return self
 
+    def _add_instance(self, caller):
+        """Add the instance to self._module_instances."""
+        self._module_instances[caller].append(self)
+
     def _unload_instance(self):
         """Base _unload_instance implementation."""
         raise NotImplementedError(
@@ -101,6 +108,17 @@ def _unload_instance(self):
             'have its own implementation of an _unload_instance method.')
 
 
+class WeakAutoUnload(AutoUnload):
+    """Subclass of AutoUnload used to store weak references to instances."""
+
+    # Create a dictionary to store AutoUnload objects in
+    _module_instances = defaultdict(WeakValueDictionary)
+
+    def _add_instance(self, caller):
+        """Add the instance to self._module_instances."""
+        self._module_instances[caller][id(self)] = self
+
+
 class GameConfigObj(ConfigObj):
     """Class used to parse specific game data."""
 
diff --git a/addons/source-python/packages/source-python/listeners/tick.py b/addons/source-python/packages/source-python/listeners/tick.py
index 6ab623ed4..6d7bb55e6 100644
--- a/addons/source-python/packages/source-python/listeners/tick.py
+++ b/addons/source-python/packages/source-python/listeners/tick.py
@@ -16,7 +16,7 @@
 
 # Source.Python Imports
 #   Core
-from core import AutoUnload
+from core import AutoUnload, WeakAutoUnload
 #   Hooks
 from hooks.exceptions import except_hooks
 #   Listeners
@@ -78,7 +78,7 @@ def add(self, delay):
 _delay_manager = _DelayManager()
 
 
-class Delay(AutoUnload):
+class Delay(WeakAutoUnload):
     """Execute a callback after a given delay."""
 
     def __init__(self, delay, callback, *args, **kwargs):
diff --git a/addons/source-python/packages/source-python/plugins/manager.py b/addons/source-python/packages/source-python/plugins/manager.py
index 9bb6c466e..1d42ca483 100644
--- a/addons/source-python/packages/source-python/plugins/manager.py
+++ b/addons/source-python/packages/source-python/plugins/manager.py
@@ -13,7 +13,7 @@
 
 # Source.Python Imports
 #   Core
-from core import _module_instances
+from core import AutoUnload, WeakAutoUnload
 #   Hooks
 from hooks.exceptions import except_hooks
 #   Plugins
@@ -182,23 +182,36 @@ def _remove_modules(self, plugin_name):
                 del sys.modules[module]
 
         # Unload AutoUnload instances
-        for module, instances in list(_module_instances.items()):
+        for module, instances in list(AutoUnload._module_instances.items()):
             if not self._is_related_module(base_name, module):
                 continue
 
-            for instance in instances:
-                try:
-                    instance._unload_instance()
-                except NotImplementedError:
-                    # Print the error to console, but allow all
-                    # other AutoUnload instances to be unloaded
-                    # and the plugin to be fully unloaded itself
-                    except_hooks.print_exception()
+            self._unload_auto_unload_instances(instances)
 
-            del _module_instances[module]
+            del AutoUnload._module_instances[module]
+
+        # Unload WeakAutoUnload instances
+        for module, instance_dict in list(WeakAutoUnload._module_instances.items()):
+            if not self._is_related_module(base_name, module):
+                continue
+
+            self._unload_auto_unload_instances(instance_dict.values())
+
+            del WeakAutoUnload._module_instances[module]
 
     @staticmethod
     def _is_related_module(base_name, module):
         """Check if a plugin's base name is related to a module name."""
         return (module.startswith('{}.'.format(base_name))
             or module == base_name)
+
+    @staticmethod
+    def _unload_auto_unload_instances(instances):
+        for instance in instances:
+            try:
+                instance._unload_instance()
+            except NotImplementedError:
+                # Print the error to console, but allow all
+                # other AutoUnload instances to be unloaded
+                # and the plugin to be fully unloaded itself
+                except_hooks.print_exception()
