V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
aladdindingding
V2EX  ›  Python

一个 Python GC 问题,虽然被解决了,但是还是不懂为什么

  •  
  •   aladdindingding · 2020-04-16 10:30:27 +08:00 · 2472 次点击
    这是一个创建于 1711 天前的主题,其中的信息可能已经有所发展或是发生改变。

    就是我们有一个 redis 的公共文件自定义了一个方法使用了 pipline,举个例子: 比如这个是一次从一个 list 一次 pop 出多个元素

    def lpopn(self, name, n):
    	"""一次 pop 多个元素"""
        pipe = self.pipeline()
        pipe.lrange(name, 0, n - 1)
        pipe.ltrim(name, n, -1)
        raw_list, is_succ = pipe.execute()
        return raw_list
    

    之前写了一个脚本用来收集 redis 中的数据入库用了这个方法,导致脚本运行时发现了内存泄漏,然后使用内存监控工具定位到了是这个 redis.client.Pipeline 这个对象占用了大量内存,之前记得有 python 的坑说的是 如果在循环引用中的对象定义了 del,那么 python gc 不能进行回收,然后我就点进 pipeline 里面看确实是定义了 del

    class Pipeline(BasePipeline, Redis):
        "Pipeline for the Redis class"
        pass
        
    class BasePipeline(object):
    		# 省略了其他方法
        def __del__(self):
            try:
                self.reset()
            except Exception:
                pass
    

    然后我就改成 脚本里面全局创建一个 pipline 对象传进这个方法,然后问题就解决了,但是不明白这个 pipeline 怎么就循环引用了呢,不知道是不是这个原因,希望有 v 友能解答一下疑惑,给点思路

    第 1 条附言  ·  2020-04-16 13:58:18 +08:00

    使用场景之前就是一个while True脚本

    while True:
        redisclient. lpopn(key,pop_count)
    

    后来改成在外面创建pipeline对象

    pipline = redisclient.pipeline()
    while True:
        redisclient. lpopn(key,pop_count,pipline)
    
    4 条回复    2020-04-17 22:38:25 +08:00
    linw1995
        1
    linw1995  
       2020-04-16 11:31:21 +08:00
    应该是执行 self.reset() 给 pipeline 带来新的引用?导致回收不了
    aladdindingding
        2
    aladdindingding  
    OP
       2020-04-16 13:52:40 +08:00
    @linw1995 reset()应该是重置 redis 事务一些参数的 应该没有带来新的引用吧
    xiaolinjia
        3
    xiaolinjia  
       2020-04-16 15:22:53 +08:00
    我看他定义了 enter 和 exit 魔法方法。不如试试
    with self.pipeline() as pipe: ?
    不过我看他代码是一样的,大概是 del 的时候出异常了?导致 reset 没执行?
    linw1995
        4
    linw1995  
       2020-04-17 22:38:25 +08:00
    @aladdindingding 你发的代码又不全。建议用 tracemalloc 看看是不是 redis.client.Pipeline, 再用 objgraph 看看又没有被生存周期长的对象引用到。没有就应该是被 `__del__` 给复活了。https://www.python.org/dev/peps/pep-0442/
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2223 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 16:05 · PVG 00:05 · LAX 08:05 · JFK 11:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.