<p>好吧,看看你的代码实际上在做什么:</p>
<pre><code>topKeys = range(16384)
table = dict((k,defaultdict(int)) for k in topKeys)
</code></pre>
<p>这将创建一个包含16384<code>defaultdict(int)</code>的dict。dict有一定的开销:dict对象本身在60到120字节之间(取决于构建中指针和ssize的大小)。这只是对象本身;除非dict少于几个项,否则数据是一个单独的内存块,在12到24字节之间,并且总是在1/2到2/3之间填充。DefaultDict要大4到8个字节,因为它们有额外的东西要存储。int是12个字节,尽管在可能的情况下可以重用它们,但代码片段不会重用大部分。因此,实际上,在一个32位的构建中,该代码段将占用<code>60 + (16384*12) * 1.8 (fill factor)</code>字节作为<code>table</code>dict,<code>16384 * 64</code>字节用于它存储为值的defaultdict,而{<cd5>}字节用于整数。所以这只是超过1.5兆字节<em>,而没有在defaultdicts</em>中存储任何内容。这是一个32位的构建;一个64位的构建将是这个大小的两倍。在</p>
<p>然后创建一个numpy数组,这实际上在内存方面非常保守:</p>
^{pr2}$
<p>这会给数组本身带来一些开销,通常的Python对象开销加上数组的维数和类型等等,但是它不会超过100字节,而且只针对一个数组。不过,它确实将<code>16384*8192</code>int32存储在512Mb中。在</p>
<p>然后你有一种非常奇特的方法来填充这个numpy数组:</p>
<pre><code>for k in topKeys:
for j in keys:
dat[k,j] = table[k][j]
</code></pre>
<p>这两个循环本身不占用太多内存,每次迭代都会重复使用。但是,<code>table[k][j]</code><em>为您请求的每个值创建一个新的Python整数</em>,并将其存储在defaultdict</em>中。创建的整数总是<code>0</code>,而且它总是被重用,但是存储对它的引用仍然会占用defaultdict中的空间:前面提到的每个条目的12个字节乘以填充因子(介于1.66和2之间),这使您在那里有接近3Gb的实际数据,而在64位的构建中则是6Gb。在</p>
<p>除此之外,由于您不断添加数据,defaultdicts必须不断增长,这意味着它们必须不断重新分配。由于Python的malloc前端(obmalloc)以及它如何在自己的块中分配较小的对象,以及进程内存在大多数操作系统上的工作方式,这意味着您的进程将分配更多而无法释放它;它实际上不会使用所有11Gb,Python将重新使用大块之间的可用内存作为defaultdict,但是总的映射地址空间将是11Gb。在</p>