Repository: marshmallow-code/marshmallow

Issue:
memory leak occurs when creating a new type
When I use the following code,  I observe that memory leak is very fast 
**MsgSchema = type('MsgSchema', (marshmallow.Schema,), {})**
```
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import marshmallow
import time
import psutil
import os

if __name__ == '__main__':
    pid = os.getpid()
    process = psutil.Process(pid)
    while True:
        MsgSchema = type('MsgSchema', (marshmallow.Schema,), {})
        mem = process.get_memory_info()[0]
        print("memory usage(%s):%d" % (pid, mem))
        time.sleep(0.01)
```

Discussion:

Patch:
diff --git a/AUTHORS.rst b/AUTHORS.rst
index d4eabed8..42b0f04b 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -87,3 +87,4 @@ Contributors (chronological)
 - Maxim Novikov `@m-novikov <https://github.com/m-novikov>`_
 - Viktor Kerkez `@alefnula <https://github.com/alefnula>`_
 - Jan Margeta `@jmargeta <https://github.com/jmargeta>`_
+- AlexV `@asmodehn <https://github.com/asmodehn>`_
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index f5dfb5d0..1cd2b3f6 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,13 @@
 Changelog
 ---------
 
+2.16.3 (unreleased)
++++++++++++++++++++
+
+Bug fixes:
+
+- Prevent memory leak when dynamically creating classes with ``type()``.
+
 2.16.2 (2018-10-30)
 +++++++++++++++++++
 
diff --git a/marshmallow/class_registry.py b/marshmallow/class_registry.py
index 0d7abbce..1cf585d0 100644
--- a/marshmallow/class_registry.py
+++ b/marshmallow/class_registry.py
@@ -48,11 +48,15 @@ def register(classname, cls):
     if classname in _registry and not \
             any(each.__module__ == module for each in _registry[classname]):
         _registry[classname].append(cls)
-    else:
+    elif classname not in _registry:
         _registry[classname] = [cls]
 
     # Also register the full path
-    _registry.setdefault(fullpath, []).append(cls)
+    if fullpath not in _registry:
+        _registry.setdefault(fullpath, []).append(cls)
+    else:
+        # If fullpath does exist, replace existing entry
+        _registry[fullpath] = [cls]
     return None
 
 def get_class(classname, all=False):


Test patch:
diff --git a/tests/test_registry.py b/tests/test_registry.py
index c4f61584..1f3c0be9 100644
--- a/tests/test_registry.py
+++ b/tests/test_registry.py
@@ -16,6 +16,66 @@ def test_serializer_has_class_registry():
     assert 'MySchema' in class_registry._registry
     assert 'MySubSchema' in class_registry._registry
 
+    # by fullpath
+    assert 'tests.test_registry.MySchema' in class_registry._registry
+    assert 'tests.test_registry.MySubSchema' in class_registry._registry
+
+
+def test_serializer_class_registry_register_same_classname_different_module():
+
+    reglen = len(class_registry._registry)
+
+    type('MyTestRegSchema', (Schema,), {'__module__': 'modA'})
+
+    assert 'MyTestRegSchema' in class_registry._registry
+    assert len(class_registry._registry.get('MyTestRegSchema')) == 1
+    assert 'modA.MyTestRegSchema' in class_registry._registry
+    #  storing for classname and fullpath
+    assert len(class_registry._registry) == reglen + 2
+
+    type('MyTestRegSchema', (Schema,), {'__module__': 'modB'})
+
+    assert 'MyTestRegSchema' in class_registry._registry
+    #  aggregating classes with same name from different modules
+    assert len(class_registry._registry.get('MyTestRegSchema')) == 2
+    assert 'modB.MyTestRegSchema' in class_registry._registry
+    #  storing for same classname (+0) and different module (+1)
+    assert len(class_registry._registry) == reglen + 2 + 1
+
+    type('MyTestRegSchema', (Schema,), {'__module__': 'modB'})
+
+    assert 'MyTestRegSchema' in class_registry._registry
+    #  only the class with matching module has been replaced
+    assert len(class_registry._registry.get('MyTestRegSchema')) == 2
+    assert 'modB.MyTestRegSchema' in class_registry._registry
+    #  only the class with matching module has been replaced (+0)
+    assert len(class_registry._registry) == reglen + 2 + 1
+
+
+def test_serializer_class_registry_override_if_same_classname_same_module():
+
+    reglen = len(class_registry._registry)
+
+    type('MyTestReg2Schema', (Schema,), {'__module__': 'SameModulePath'})
+
+    assert 'MyTestReg2Schema' in class_registry._registry
+    assert len(class_registry._registry.get('MyTestReg2Schema')) == 1
+    assert 'SameModulePath.MyTestReg2Schema' in class_registry._registry
+    assert len(class_registry._registry.get('SameModulePath.MyTestReg2Schema')) == 1
+    #  storing for classname and fullpath
+    assert len(class_registry._registry) == reglen + 2
+
+    type('MyTestReg2Schema', (Schema,), {'__module__': 'SameModulePath'})
+
+    assert 'MyTestReg2Schema' in class_registry._registry
+    #  overriding same class name and same module
+    assert len(class_registry._registry.get('MyTestReg2Schema')) == 1
+    assert 'SameModulePath.MyTestReg2Schema' in class_registry._registry
+    #  overriding same fullpath
+    assert len(class_registry._registry.get('SameModulePath.MyTestReg2Schema')) == 1
+    #  overriding for same classname (+0) and different module (+0)
+    assert len(class_registry._registry) == reglen + 2
+
 
 class A: