Skip to content

Commit cce1e2b

Browse files
agrenottacozzette
authored andcommitted
Fix PyUnknownFields memory leak (protocolbuffers#7928)
Properly release internal data structure on deallocation. Fix protocolbuffers#7301
1 parent 4928583 commit cce1e2b

File tree

2 files changed

+25
-0
lines changed

2 files changed

+25
-0
lines changed

python/google/protobuf/internal/unknown_fields_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@
3939
import unittest2 as unittest #PY26
4040
except ImportError:
4141
import unittest
42+
import sys
43+
try:
44+
import tracemalloc
45+
except ImportError:
46+
# Requires python 3.4+
47+
pass
4248
from google.protobuf import map_unittest_pb2
4349
from google.protobuf import unittest_mset_pb2
4450
from google.protobuf import unittest_pb2
@@ -312,6 +318,24 @@ def testClear(self):
312318
self.assertIn('UnknownFields does not exist.',
313319
str(context.exception))
314320

321+
@unittest.skipIf((sys.version_info.major, sys.version_info.minor) < (3, 4),
322+
'tracemalloc requires python 3.4+')
323+
def testUnknownFieldsNoMemoryLeak(self):
324+
# Call to UnknownFields must not leak memory
325+
nb_leaks = 1234
326+
def leaking_function():
327+
for _ in range(nb_leaks):
328+
self.empty_message.UnknownFields()
329+
tracemalloc.start()
330+
snapshot1 = tracemalloc.take_snapshot()
331+
leaking_function()
332+
snapshot2 = tracemalloc.take_snapshot()
333+
top_stats = snapshot2.compare_to(snapshot1, 'lineno')
334+
tracemalloc.stop()
335+
# There's no easy way to look for a precise leak source.
336+
# Rely on a "marker" count value while checking allocated memory.
337+
self.assertEqual([], [x for x in top_stats if x.count_diff == nb_leaks])
338+
315339
def testSubUnknownFields(self):
316340
message = unittest_pb2.TestAllTypes()
317341
message.optionalgroup.a = 123

python/google/protobuf/pyext/unknown_fields.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ static void Dealloc(PyObject* pself) {
142142
}
143143
Py_CLEAR(self->parent);
144144
self->~PyUnknownFields();
145+
Py_TYPE(pself)->tp_free(pself);
145146
}
146147

147148
static PySequenceMethods SqMethods = {

0 commit comments

Comments
 (0)