Python进程使用内存后不释放

背景:

使用python进行大量的数据操作过程中RSS占用(几个G,Python的GC会频繁地malloc/free),发现RSS内存不释放。

  1. 排查代码后,没有发现内存泄露的情况,GC也是默认开启的,甚至代码将数据库数据读出来遍历一遍后程序结束,内存一直存在不释放。
  2. 使用tracemalloc,objectgraph调试,Python对象的产生和释放并没有啥异常。
  3. Python的对象内存管理是基于引用计数的(refcnt为0直接decref回收),python内存池也没发现大内存驻留,操作系统brk和mmap也并没有不释放内存。

在调测中也发现,这个问题给人的整体感觉不是代码哪里有问题,查阅相关资料后,发现可能是Glibc优化问题。

python的内存机制,频繁通过系统调用获取和释放内存对性能消耗很大的,python将对象销毁后,并没有立即将这部分内存返回给操作系统,而是加到了自己维护的空闲内存池中。从系统层面来看,这步扥内存已经被python进程占用了,但是从python解释器的角度来看,这部分是free的,可以用于生成新的对象。

那么我们在Python中如何手动分配释放内存?

我们使用进程内存隔离的能力直接管理内存,父进程只负责进程池管理,子进程干完事情,直接退出,或过期重新拉起,或者超过内存阈值就干掉,就可以了(当然最好是业务处理不是很频繁的情况下)。

多进程处理参考:multiprocessing --- 基于进程的并行 — Python 3.10.0 文档

Process 类

在 multiprocessing 中,通过创建一个 Process 对象然后调用它的 start() 方法来生成进程。 Process 和 threading.Thread API 相同。 一个简单的多进程程序示例是:

from multiprocessing import Process

def f(name):
    print('hello', name)

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

常见问题: 

实际业务中会经常用到数据库等长连接的操作,如数据库等,可能会报如下错误:

ERROR:base.py:Line 705:_finalize_fairy:Exception during reset or similar

Traceback (most recent call last):
  File "/home/tsalazar/.cache/pypoetry/virtualenvs/ufos--PZ7y9g--py3.8/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 697, in _finalize_fairy
    fairy._reset(pool)

  File "/home/tsalazar/.cache/pypoetry/virtualenvs/ufos--PZ7y9g--py3.8/lib/python3.8/site-packages/sqlalchemy/pool/base.py", line 893, in _reset
    pool._dialect.do_rollback(self)

  File "/home/tsalazar/.cache/pypoetry/virtualenvs/ufos--PZ7y9g--py3.8/lib/python3.8/site-packages/sqlalchemy/engine/default.py", line 558, in do_rollback
    dbapi_connection.rollback()

psycopg2.OperationalError: SSL error: decryption failed or bad record mac

解决办法:不要将长连接对象通过参数传递,在函数内部链接,使得进程之间互不影响。 

参考:

  • 《python源码剖析》Cpython代码
  • MySQL 内存管理初探
  • glibc malloc常驻内存不释放问题抽象

你可能感兴趣的:(python,python,Process,内存管理,multiprocessing)