python中for循环的内存分配

Posted

技术标签:

【中文标题】python中for循环的内存分配【英文标题】:Memory allotment of for loop in python 【发布时间】:2015-08-10 18:53:54 【问题描述】:

我对 python 对函数的内存使用感到困惑。 我正在运行一个函数,其中返回 pandas 数据帧 (1161 X 240),参数为 (bamfile, pandas.Dataframe(1161 X 50))。

现在我将通过分析器给出内存使用情况:

Line #    Mem usage    Increment   Line Contents
================================================
   120  983.363 MiB    0.000 MiB   @profile
   121                             def overlapping_peaks_distribution(bam_peak1, overlap_df):
   122                                 '''
   123                                 Returns dataframe for tag count distribution for overlapping peaks within 500bp (+,-) from summit.
   124                                 This function also considers the gene transcrition direction.
   125                                 :param bam_peak1:
   126                                 :param overlap_df:
   127                                 :return:
   128                                 '''
   129  983.363 MiB    0.000 MiB       import pandas as pd
   130  983.363 MiB    0.000 MiB       import sys
   131  983.363 MiB    0.000 MiB       peak_distribution_sample = pd.DataFrame()
   132  983.363 MiB    0.000 MiB       print 'Process: Feature extraction from BAM started'
   133 1783.645 MiB  800.281 MiB       for ind, row in overlap_df.iterrows():
   134 1782.582 MiB   -1.062 MiB           sys.stdout.write("\rFeature extraction for peak:%d" % ind)
   135 1782.582 MiB    0.000 MiB           sys.stdout.flush()
   136 1782.582 MiB    0.000 MiB           chr = str(row['chr'])
   137 1782.582 MiB    0.000 MiB           orientation = row['Next transcript strand']
   138 1782.582 MiB    0.000 MiB           middle = row['start'] + row['summit']
   139 1782.582 MiB    0.000 MiB           start = middle - 3000
   140 1782.582 MiB    0.000 MiB           stop = start + 50
   141 1782.582 MiB    0.000 MiB           list_sample1 = []
   142                                     #total_tags = int(bam_peak1.mapped) will get total no of mapped reads
   143                             
   144 1782.586 MiB    0.004 MiB           for i in range(0, 120):
   145 1782.586 MiB    0.000 MiB               tags1 = bam_peak1.count(chr, start, stop)
   146 1782.586 MiB    0.000 MiB               start = stop
   147 1782.586 MiB    0.000 MiB               stop = start + 50  # divide peaks into length of 25 bp
   148 1782.586 MiB    0.000 MiB               list_sample1.append(tags1)
   149 1782.586 MiB    0.000 MiB           if orientation > 0:    # Direction gene transcription
   150                                         #print 'Towards 5 prime'
   151 1780.883 MiB   -1.703 MiB               peak_distribution_sample = peak_distribution_sample.append(pd.Series(list_sample1), ignore_index=True)
   152                                     else:
   153                                         #print 'Towards 3 prime'
   154 1783.645 MiB    2.762 MiB               peak_distribution_sample = peak_distribution_sample.append(pd.Series(list_sample1[::-1]), ignore_index=True)
   155                                 #print peak_distribution_sample
   156 1783.645 MiB    0.000 MiB       return peak_distribution_sample

我不明白为什么在 第 133 行 会增加 800MB(疯狂)。这占用了我记忆中的所有空间。不知道这是不是我的错?

我使用对象图来查找内存泄漏。 函数启动前的对象数:

(Pdb) objgraph.show_most_common_types()
function                   15293
tuple                      4115
dict                       3086
cell                       2670
list                       2107
weakref                    1834
wrapper_descriptor         1760
builtin_function_or_method 1655
getset_descriptor          1235
type                       1232

函数完成后的对象数。

(Pdb) import objgraph
(Pdb) objgraph.show_growth()
function                      16360     +1067
dict                           3546      +460
list                           2459      +354
tuple                          4414      +306
getset_descriptor              1508      +273
builtin_function_or_method     1895      +240
weakref                        2049      +215
module                          593      +123
wrapper_descriptor             1877      +117
type                           1341      +109

我们可以看到对象的显着增加。 我还制作了一些图表。

我相信 红色字体框应该被释放,但它们没有。

【问题讨论】:

很有可能overlap_df.iterrows() 在开始第一次迭代之前将其完全加载到内存中。 github.com/pydata/pandas/issues/7683 这可能是个问题,但如何释放该内存。 【参考方案1】:

您确定它没有显示overlap_df 的总大小吗?

这里有类似的东西:

144 1782.586 MiB 0.004 MiB for i in range(0, 120):

Profiler 将 120 个整数列表的总大小显示为 400 KB。

【讨论】:

这意味着overlap_df的大小为800MB。这是不可能的,因为在物理内存上它需要 8MB 的空间。我在每次迭代时都读到它分配内存但不释放它。最后函数完成打印 out_of_memory()。我也试过 gc.collect() 但它没有效果。嵌套循环使情况变得更糟。【参考方案2】:

我发现了内存泄漏。这是因为第三方模块 (pysam) 存在内存泄漏。

【讨论】:

以上是关于python中for循环的内存分配的主要内容,如果未能解决你的问题,请参考以下文章

广播会分配大量内存,而 for 循环不会

for 循环期间的动态分配会造成内存泄漏吗?

在硬件级别的“for循环”中会发生啥?内存是自动分配的吗? (C++)[关闭]

malloc分配内存为多大?

OpenCV 中 Mat 等结构的内存分配

内存分配,输出垃圾