0

With this code, I just make python create some objects, at the same time some ps -auxw are done to monitor process RSS memory :

import gc
import os


class A:
    __slots__ = 'a', 'b', 'c', 'd'
    def __init__(self, a, b, c, d):
        self.a = a
        self.b = b
        self.c = c
        self.d = d


print('Memory status before')
print('====================')
print('GC tracked objects count =', len(gc.get_objects()))
print(gc.get_stats())
os.system('ps -auxw | grep "python ./memory_test.py" | grep -v grep')

# Using dict comprehension instead of list comprehension because taking more memory
# and free less after gc.collect() (why ??)
some_objects = {
    i: A(1, 2, 3, 4)
    for i in range(1_000_000)
}

print('\n\nMemory status after creating some objects')
print('=========================================')
print('GC tracked objects count =', len(gc.get_objects()))
print(gc.get_stats())
os.system('ps -auxw | grep "python ./memory_test.py" | grep -v grep')

some_objects.clear()
del globals()['some_objects']
gc.collect()

print('\n\nMemory status after clearing objects')
print('====================================')
print('GC tracked objects count =', len(gc.get_objects()))
print(gc.get_stats())
os.system('ps -auxw | grep "python ./memory_test.py" | grep -v grep')

print('--- end ---')

With python 3.6 I get this output :

Memory status before
====================
GC tracked objects count = 7007
[{'collections': 15, 'collected': 99, 'uncollectable': 0}, {'collections': 1, 'collected': 0, 'uncollectable': 0}, {'collections': 0, 'collected': 0, 'uncollectable': 0}]
elapouya 14256  0.0  0.0  36868  9196 pts/1    S+   18:58   0:00 python ./memory_test.py


Memory status after creating some objects
=========================================
GC tracked objects count = 1006950
[{'collections': 1315, 'collected': 99, 'uncollectable': 0}, {'collections': 119, 'collected': 0, 'uncollectable': 0}, {'collections': 8, 'collected': 0, 'uncollectable': 0}]
elapouya 14256 50.0  0.4 195304 161400 pts/1   S+   18:58   0:00 python ./memory_test.py


Memory status after clearing objects
====================================
GC tracked objects count = 6949
[{'collections': 1315, 'collected': 99, 'uncollectable': 0}, {'collections': 119, 'collected': 0, 'uncollectable': 0}, {'collections': 9, 'collected': 0, 'uncollectable': 0}]
elapouya 14256 54.0  0.0  51172 17448 pts/1    S+   18:58   0:00 python ./memory_test.py
--- end ---

One can see that just before creating objects, python interpreter is using 9196 KB of resident memory, then 161400 KB after creating one million objects. But after clearing all objects references and asking the garbage collector to free memory, the linux python process is not freeing all memory : it still uses 17448 KB of resident memory, I was expecting around 9200 KB. NOTE : a time.sleep(10) does not help to get more memory free.

Why Python does not free totally unreferenced objects after a garbage collection ?

Eric
  • 4,821
  • 6
  • 33
  • 60
  • I am not familiar with cpython internals, but you can make one more simple test adding `sleep(5)` before obtaining final memory status. – Olvin Roght Mar 12 '22 at 18:25
  • 1
    There are some interesting answers to this [question](https://stackoverflow.com/questions/15455048/releasing-memory-in-python) – quamrana Mar 12 '22 at 18:26
  • NOTE : a `time.sleep(10)` does not help to get more memory free – Eric Mar 12 '22 at 18:40

0 Answers0