Blog.GC

一段调试Python代码的经历: gc, profiler & more

| Comments

最近在跑一段实验代码的时候发现跑了一阵子之后内存就超过了系统内存(单进程1G左右,共46个进程并行),导致性能骤降。然而刚开始运行的时候占用并不多(大概170M左右)。 好吧,下意识就想到了可能是内存泄露了。一顿爆改,手动清空各种dictlist,然而并没有什么卵用。于是请出了gc

# 环境Python 3.4
import gc
gc.set_debug(gc.DEBUG_LEAK)

# 一坨代码

print("The number of unreachable objects found: %s" % gc.collect())

然而也没有什么用,unreachable的输出全为0,并没有内存泄露。

难道是multiprocessing.Pool的锅?谷歌了一通,发现确实有可能,默认情况下进程池的子进程在任务结束后并不会结束,导致内存并不会完全释放。于是接着改,添加maxtasksperchild参数,让子进程运行10个任务就结束然后重开子进程。

from multiprocessing import Pool
MAX_TASK_PER_CHILD = 10 # 激进点,设成1000就没意思了(误

with closing(Pool(processes=46, maxtasksperchild=MAX_TASK_PER_CHILD)) as pool:
    # 一坨代码
    pool.close()
    pool.join()

也没什么卵用,时间倒是过去两天了,有点懵逼。

还是先用单个任务运行一下吧,用tracemallocmemory_profiler监测一下内存

import tracemalloc
tracemalloc.start()

# 一坨代码
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

print("[ Top 10 ]")
for stat in top_stats[:10]:
    print(stat)
pip install memory_profiler
mprof run test.py
mprof plot

监测下来单个任务就要1G左右,难怪并行的时候耗尽内存,然后看tracemalloc的输出

[ Top 10 ]
src/core/player.py:73: size=521 MiB, count=3072, average=174 KiB
src/core/player.py:72: size=521 MiB, count=3072, average=174 KiB
src/core/player.py:35: size=719 KiB, count=6969, average=106 B
src/core/player.py:38: size=335 KiB, count=825, average=416 B
src/core/player.py:37: size=335 KiB, count=825, average=416 B
src/core/player.py:36: size=335 KiB, count=825, average=416 B
src/core/player.py:19: size=191 KiB, count=2031, average=96 B
src/core/player.py:22: size=107 KiB, count=1209, average=91 B
src/core/player.py:26: size=107 KiB, count=1208, average=90 B
src/core/player.py:25: size=107 KiB, count=1208, average=90 B

两个list占了99.9%的内存,然而这两个list只存0和1,把list换成bytearray之后,内存占用即变为原来的1/8。系统内存就够用了。

问题解决之后,回过头来看解决的过程,感觉自己好鱼……

我差不多是条咸鱼了

Comments

comments powered by Disqus