🌐 AI搜索 & 代理 主页
Skip to content

Commit 78c2504

Browse files
committed
Fixed #36701 -- Fixed memory leak in ModelState reference cycles.
1 parent 334308e commit 78c2504

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

django/db/models/base.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,12 @@ def __getstate__(self):
494494
state.pop("peers", None)
495495
return state
496496

497+
def __del__(self):
498+
try:
499+
self.fields_cache.clear()
500+
except AttributeError:
501+
pass
502+
497503

498504
class Model(AltersData, metaclass=ModelBase):
499505
def __init__(self, *args, **kwargs):

tests/model_regress/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,11 @@ class Model2(models.Model):
6060

6161
class Model3(models.Model):
6262
model2 = models.ForeignKey(Model2, models.CASCADE, unique=True, to_field="model1")
63+
64+
65+
class CycleParent(models.Model):
66+
pass
67+
68+
69+
class CycleChild(models.Model):
70+
parent = models.OneToOneField(CycleParent, on_delete=models.CASCADE)

tests/model_regress/test_state.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
1+
import gc
2+
13
from django.db.models.base import ModelState, ModelStateFieldsCacheDescriptor
24
from django.test import SimpleTestCase
5+
from django.test.utils import garbage_collect
6+
7+
from .models import CycleChild, CycleParent
38

49

510
class ModelStateTests(SimpleTestCase):
611
def test_fields_cache_descriptor(self):
712
self.assertIsInstance(ModelState.fields_cache, ModelStateFieldsCacheDescriptor)
13+
14+
def test_one_to_one_field_cycle_collection(self):
15+
self.addCleanup(gc.set_debug, gc.get_debug())
16+
gc.set_debug(gc.DEBUG_SAVEALL)
17+
18+
def create_cycle():
19+
p = CycleParent()
20+
c = CycleChild(parent=p)
21+
_ = c.parent
22+
return id(p)
23+
24+
p_id = create_cycle()
25+
26+
garbage_collect()
27+
28+
leaked = [obj for obj in gc.garbage if id(obj) == p_id]
29+
self.assertEqual(leaked, [])
30+
31+
del gc.garbage[:]

0 commit comments

Comments
 (0)