Python中__del__函数及内存释放问题

前言

最近遇到了内存释放问题,由于处理的文件有23k之多,互相引用,以至于程序占用的内存可以达到33G。

del操作

虽然使用了 =del= ,但已经占据的内存依旧没有立即释放,因为可能有打开的文件资源没有 =close()= ,或者对象的引用计数还大于0等等。

最后内存还是一直占用,直到程序退出才一次性释放,尝试通过定义 =del()= 函数来清理。

del函数

对需要的对象 =适当的= 添加 =del()= 函数,但这个函数并不是一定就有效(object.del):

1
2
3
4
5
6
7
8
9
10
11
12
object.__del__(self)
Called when the instance is about to be destroyed. This is also called a destructor.
If a base class has a __del__() method, the derived class’s __del__() method, if any, must explicitly call
it to ensure proper deletion of the base class part of the instance.
Note that it is possible (though not recommended!) for the __del__() method to postpone destruction of the
instance by creating a new reference to it. It may then be called at a later time when this new reference is deleted.
It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.
Note:
del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and
the latter is only called when x‘s reference count reaches zero.

gc模块

对gc模块不是很熟悉,看了点资料,加了一句代码来主动回收内存:

1
2
import gc
gc.collect()

发现加了这个,结合 =del()= 来使用,内存的确及时释放掉了。

测试

处理100个文件

测试1: 处理100个文件

del gc 最大占用内存 内存变化 耗时
N N 1.9G 持续增加 0m24.102s
Y Y 1.9G 保持不变 1m11.930s
Y N 1.9G 保持不变 0m26.656s
N Y 1.9G 保持不变 1m41.994s

结论:

  • 测试文件小,发现默认的Python行为反而是最快的。

处理10000个文件

测试2: 处理10000个文件

del gc 最大占用内存 内存变化 耗时
N N 10G 持续增加 6m33.719s
Y N 1.9G 保持不变 6m47.577s
N Y 1.9G 保持不变 超过12m
Y Y 1.9G 保持不变 超过8m

结论:

  • 测试文件多到一定程度,仅仅使用 =del()= 可以释放掉内存,但执行速度会因此变慢一点
  • 但如果引入 =gc模块= ,速度则会变慢很多,但对gc的时候进行优化,应该有很大的提升空间。

使用PyPy

测试3: 处理10000个文件,对比PyPy

Python版本 del gc 最大占用内存 内存变化 耗时
CPython 2.7 N N 10G 持续增加 6m33.719s
PyPy N N 8G 持续增加 3m37.196s
CPython 2.7 Y N 1.9G 保持不变 6m47.577s
PyPy Y N 超过2.8G 持续增加
CPython 2.7 N Y 1.9G 保持不变 超过12m
PyPy
CPython 2.7 Y Y 1.9G 保持不变 超过8m
PyPy Y Y 1.2G 保持不变 超过4m

结论:

  • 通过这个简单地测试来看,PyPy的速度的确提高很多
  • 但不结合gc模块,对 =del()= 释放的内存没有及时回收

后记

上面的测试也是任性而为,不能说明什么问题;由于之前程序最大的问题是占用内存不释放,速度已经在可以接受范围之内,因此通过 =del()= 函数得到解决后,则告一段落。

要提高速度,还可以使用Cython扩展模块或者引入多线程等等方法,但通过改变程序工作方式,也取得了不错的效果,同时兼备了一定的灵活性。

吴羽舒 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!