背景
构建一个大字典,并往其中增减元素,但是发现整体的内存消耗并没有因此而维持稳定状态,而是不断增加。
问题解析
Python中的字典,只有不再使用的时候才会释放对应的内存。在使用 pop 或者 delete 删除字典中的item(或者说entry)后,为了保证hash table 探测链的完整,那个被删除的entry只是被标记成了空,并没有真正被删除掉,所以该字典的内存占用没有得到释放。这是为了避免多度重建hash table。
释放内存
那如何释放字典的内存?现已知的方案是创建或者拷贝一个旧字典再覆盖掉新字典。具体示例如下:
import sys
import gc
import copy
a = {}
print("init empty dict memory size={} bytes".format(sys.getsizeof(a)))
for i in range(10**6):
a[i] = i
print("after set value, dict memory size={} bytes".format(sys.getsizeof(a)))
for i in range(10**6):
del a[i]
# a.pop(i)
print("after del, dict memory size={} bytes".format(sys.getsizeof(a)))
a_new = dict(a)
print("after init a new one, dict memory size={} bytes".format(sys.getsizeof(a_new)))
b = copy.copy(a)
print("after copy a new one, dict memory size={} bytes".format(sys.getsizeof(b)))
c = copy.deepcopy(a)
print("after deepcopy a new one, dict memory size={} bytes".format(sys.getsizeof(c)))
运行结果如下:
init empty dict memory size=240 bytes
after set value, dict memory size=41943144 bytes
after del, dict memory size=41943144 bytes
after init a new one, dict memory size=240 bytes
after copy a new one, dict memory size=240 bytes
after deepcopy a new one, dict memory size=240 bytes
字典clear操作后的内存占用比完全新建的时候小?
在实验过程发现,字典调用 clear 操作后的内存占用比新建一个字典的内存占用小。示例如下:
dict = {}
print(sys.getsizeof(dict)) # 240, 这因为新的字典的 size 是 PyDict_MINSIZE
dict.clear()
print(sys.getsizeof(dict)) # 72
这是因为新建dictionary是按照PyDict_MINSIZE 分配keyspace。当调用.clear()
函数后,keyspace 被重新分配到一个静态的空keyspace: Py_EMPTY_KEYS,此时的dictionary是真的empty。