From c9edb9dccbf09d0bd5fe17273f4f394b1f79c01c Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 15 Mar 2017 16:47:56 +0100 Subject: [PATCH] [FIX] registry: use a weak dictionary for `model_cache` to avoid memory leaks The class attribute `model_cache` refers to model classes, which refer to their own registry. This cache potentially keeps all past registries alive! --- odoo/models.py | 2 +- odoo/modules/registry.py | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/odoo/models.py b/odoo/models.py index 06d40e9e21d..77fcc333531 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -2817,7 +2817,7 @@ class BaseModel(object): self._add_magic_fields() cls._proper_fields = set(cls._fields) - cls.pool.model_cache[cls._model_cache_key] = cls + cls.pool.model_cache[cls._model_cache_key] = cls # 2. add custom fields self._add_manual_fields(partial) diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py index 6e5378d33c8..1926ad33776 100644 --- a/odoo/modules/registry.py +++ b/odoo/modules/registry.py @@ -7,6 +7,7 @@ from collections import Mapping, defaultdict, deque from contextlib import closing from operator import attrgetter +from weakref import WeakValueDictionary import logging import os import threading @@ -30,6 +31,9 @@ class Registry(Mapping): _lock = threading.RLock() _saved_lock = None + # a cache for model classes, indexed by their base classes + model_cache = WeakValueDictionary() + @lazy_classproperty def registries(cls): """ A mapping from database names to registries. """ @@ -177,12 +181,6 @@ class Registry(Mapping): """ Add or replace a model in the registry.""" self.models[model_name] = model - @lazy_classproperty - def model_cache(cls): - """ A cache for model classes, indexed by their base classes. """ - # we cache 256 classes per registry on average - return LRU(cls.registries.count * 256) - @lazy_property def field_sequence(self): """ Return a function mapping a field to an integer. The value of a