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 ?