Skip to main content
 首页 » 编程设计

python之为什么数据字典的内存开销会是磁盘上相同数据的两倍

2025年05月04日23dudu

我正在尝试从包含大约 300 万个 json 序列化对象的文件中加载数据集。每个对象都是一个包含各种类型的大型嵌套字典——整数、 float 、列表和其他字典。

磁盘上文件的大小约为 60GB。我有超过 128GB 的​​内存,所以我应该能够在内存中容纳整个集合。但是,当我使用以下代码将数据加载到一个大字典中时,已用内存的大小增加到至少 110GB(它甚至可能变得更大,但我在它增长之前停止了脚本。)

什么可以解释我在尝试加载此数据时看到的内存开销?为什么磁盘上的 60GB 会转化为内存中的 110GB 或更多?据我所知,这里唯一的开销应该是为对象创建列表容器的结果,以及为这些列表分配结果字典中的键名的结果。这不可能占数据本身几乎两倍的内存 - 是吗?

def load_by_geohash(file, specificity=7): 
    results = defaultdict(list) 
    filename = os.path.join(DATADIR, file) 
 
    with open(filename, 'r') as f: 
        updates = (json.loads(line) for line in f) 
        for update in updates: 
            geo_hash = update['geohash'][:specificity] 
            results[geo_hash].append(update) 
 
    return results 

请您参考如下方法:

是的,它很容易做到。考虑一个字符串列表的简单情况:

>>> import json 
>>> from sys import getsizeof 
>>> x = ['a string', 'another string', 'yet another'] 
>>> sum(map(getsizeof, x)) + getsizeof(x) 
268 
>>> len(json.dumps(x).encode()) 
45 
>>> 

在 Python 中,一切皆对象。因此,每个(好吧,大多数)单个对象都有至少 sys.getsizeof(object()) 开销。请注意,我的系统上有一个空字符串:

>>> getsizeof('') 
49 

请注意,对于 dict 对象,这种差异更大,请考虑:

>>> d 
{'a': 'a string', 'b': 'another string', 'c': 'yet another'} 
>>> sum(map(getsizeof, d)) + sum(map(getsizeof, d.values())) + getsizeof(d) 
570 
>>> len(json.dumps(d).encode()) 
60 

对于一个空的字典来说,这是非常巨大的:

>>> getsizeof({}), len(json.dumps({}).encode()) 
(240, 2) 

现在,有多种选项可以更紧凑地存储数据。但这取决于您的用例。

Here是关于许多词典的内存使用的相关问题。还有一个使用 numpy 数组和 namedtuple 对象来更紧凑地存储数据的示例。请注意,使用 namedtuple 对象可能是您所需要的,内存节省可能是巨大的,因为您不需要为键存储实际的字符串对象。如果您的子字典结构是规则的,我建议用嵌套的 namedtuple 对象替换那些嵌套的 update 字典。