Commit 11e2b506 authored by Arnaud Fontaine's avatar Arnaud Fontaine

Portal Type as Classes: Fix memory leak: reset erp5.* classes were never GC (#20170126-19C38D8).

zope.{interface,component} implement Interfaces through __implements__,
__implemented__ (both implementedBy instances) and __provides__ (ClassProvides
instance) attributes set on the class itself through implementedByFallback
(zope.interface.declarations).

However, this implementation creates circular references by referencing the
class itself and thus erp5.* classes (and all its Accessors instances) were
never GC even after a reset.

When running testXHTML and installing the Unit Tests bt5s:
  * After 10 resets:  21MB leak (~  7% of Zope process USS).
  * After 20 resets:  70MB leak (~ 18% of Zope process USS).
  * After 28 resets: 122MB leak (~ 26% of Zope process USS).
parent 6316799d
......@@ -374,6 +374,21 @@ def synchronizeDynamicModules(context, force=False):
try:
for class_name, klass in inspect.getmembers(erp5.portal_type,
inspect.isclass):
# Zope Interface is implemented through __implements__,
# __implemented__ (both implementedBy instances) and __provides__
# (ClassProvides instance) attributes set on the class by
# zope.interface.declarations.implementedByFallback.
#
# However both implementedBy and ClassProvides instances keep a
# reference to the class itself, thus creating a circular references
# preventing erp5.* classes to be GC even when not being actually used
# anywhere anymore after a reset.
for k in klass.mro():
if k.__module__.startswith('erp5.'):
for attr in ('__implements__', '__implemented__', '__provides__'):
if k.__dict__.get(attr) is not None:
delattr(k, attr)
klass.restoreGhostState()
# Clear accessor holders of ZODB Property Sheets and Portal Types
......@@ -402,3 +417,12 @@ def synchronizeDynamicModules(context, force=False):
cache_tool = getattr(portal, 'portal_caches', None)
if cache_tool is not None:
cache_tool.clearCache()
# Clear Zope Component Registries (Zope Adapters/Utilities cache lookup)
# because it contains references to reset dynamic classes (which prevents
# them from being GC and may create inconsistencies when Interfaces have
# been changed)
import zope.component
gsm = zope.component.getGlobalSiteManager()
gsm.adapters.changed(gsm)
gsm.utilities.changed(gsm)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment