大佬们,有谁研究过 Python importlib 机制? sys.modules 缓存太大了 - V2EX
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
iyaozhen
V2EX    Python

大佬们,有谁研究过 Python importlib 机制? sys.modules 缓存太大了

  •  
  •   iyaozhen
    iyaozhen 2022-03-01 15:27:27 +08:00 4739 次点击
    这是一个创建于 1319 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有个业务需要动态加载很多本地文件( pb 生成的)

    Python 自己的 sys.modules 缓存,加载过的都会缓存。而且是多级
    比如一个模块是 a.b.c ,会生成 a a.b a.b.c 缓存

    每个 request 是共用这个,会让 sys.modules 这个缓存越来越大,线上 20 来个实例,一共占用了 700 多 G 内存

    https://docs.python.org/zh-cn/3/reference/import.html#the-module-cache
    第 1 条附言    2022-03-02 16:38:28 +08:00
    稍等解释下为什么这么大
    1. 动态加载的是 pb 生成的 xxx_pb2.py 文件,熟悉的同学都了解,pb 生成的代码冗余信息很多,很大,甚至 pb 源文件内容都包含在里面,我大概看了下生成的文件大的有 2.4M 小的几十 k 。这只是 py 文件的大小,再加载到内存里应该更大(没实际算过)
    而且 Python 加载机制不只是 sys.modules ,别的地方也有占用

    2. 服务运行模式选的不对,服务启动的时候开了 10 个 worker 进程,会导致占用大了 10 倍,其实开 1 个 worker 进程就行
    27 条回复    2022-03-02 16:25:30 +08:00
    keepeye
        1
    keepeye  
       2022-03-01 15:39:19 +08:00
    帮顶,试试 importlib.invalidate_caches() 不知道是不是用来清缓存的
    iyaozhen
        2
    iyaozhen  
    OP
       2022-03-01 15:51:10 +08:00
    @keepeye
    importlib.invalidate_caches()
    Invalidate the internal caches of finders stored at sys.meta_path. If a finder imlements invalidate_caches() then it will be called to perform the invalidation. This function should be called if any modules are created/installed while your program is running to guarantee all finders will notice the new module’s existence.

    这个主要是发现新模块的,比如 a.b.c 多了个 a.b.c1

    实际试了下也不会清 sys.modules
    ruanimal
        3
    ruanimal  
       2022-03-01 15:54:11 +08:00   1
    https://stackoverflow.com/questions/3105801/unload-a-module-in-python

    应该是不行的,感觉服务拆分下吧
    iyaozhen
        4
    iyaozhen  
    OP
       2022-03-01 15:58:44 +08:00
    @ruanimal 拆分也没用吧 总量不会变。耗内存
    hhhhhh123
        5
    hhhhhh123  
       2022-03-01 17:28:30 +08:00
    用一个删一个 都在一个字典里面, 这样就行了, 我一起优化过一个服务刚好就是 7 ,8 k 得根据入参导入不同得文件 然后获取里面得函数 进行调用 一样得道理 , 用完即删除 无非是用时间换空间
    zhengxiaowai
        6
    zhengxiaowai  
       2022-03-01 17:57:04 +08:00
    import 有个 hook ,感觉可以搞一下,盲猜一下思路大约是,启动不加载那些生成的 pb ,只有在用到的时候利用 hook 捕获一下,然后做一下动态导入,同时这个 hook 里还有一个 lru ,比如 500 个,超出 500 个的从 sys.module 中给他扔掉。

    ------

    BTW, builder 这个服务不用太管它,,偶尔还会有内存泄露问题 :-)
    joApioVVx4M4X6Rf
        7
    joApioVVx4M4X6Rf  
       2022-03-01 20:32:34 +08:00
    楼主解决了吗
    alphanow
        8
    alphanow  
       2022-03-01 21:39:50 +08:00 via Android   1
    sys.modules 只是一个 Python 引用列表,真正的对象是在堆记得,除了 Python 代码本身,底层的 C 代码有时也会对其存在引用。所以直接删除里面的条目可能是不起作用的。
    所以有两个可能的解决方案:
    用文件读取处理的方式生成一个对象,避免 import
    开一个独立的 process 处理数据,用完直接干掉
    iyaozhen
        9
    iyaozhen  
    OP
       2022-03-01 21:47:47 +08:00
    @hhhhhh123 这样细节上不好操作
    特别是请求量大的时候,比如 a.b.c1 a.b.c2 cache 里面有 a.b ,如果 a.b.c1 后删除 a.b cache ,刚好 a.b.c2 内部在用的时候取不到了
    imn1
        10
    imn1  
       2022-03-01 21:48:54 +08:00
    700G 内存,羡慕
    iyaozhen
        11
    iyaozhen  
    OP
       2022-03-01 21:52:53 +08:00
    @zhengxiaowai 哈哈哈,老哥 你这留下的坑,请打开飞书交流

    嗯嗯 hook 的思路想过,还得具体试试
    iyaozhen
        12
    iyaozhen  
    OP
       2022-03-01 21:53:21 +08:00
    @v2exblog 还没呢 得先多想几个解决方案
    iyaozhen
        13
    iyaozhen  
    OP
       2022-03-01 22:01:21 +08:00
    @alphanow 「用文件读取处理的方式生成一个对象」这是什么操作,但可能也不行 因为文件内部还有 import 嵌套

    「开一个独立的 process 处理数据,用完直接干掉」改成多进程模型,这倒好像可以,但其实就用不上缓存了,不知道性能如何
    Cooky
        14
    Cooky  
       2022-03-01 22:01:22 +08:00
    整个多进程,让子进程处理,处理完了关了重开?
    iyaozhen
        15
    iyaozhen  
    OP
       2022-03-01 22:02:01 +08:00
    @imn1 运维已经要从我工资里面扣了 (开玩笑
    iyaozhen
        16
    iyaozhen  
    OP
       2022-03-01 22:02:57 +08:00
    @Cooky 我试试
    flynaj
        17
    flynaj  
       2022-03-01 22:13:55 +08:00 via Android
    要性能你上 golang,python,,
    sujin190
        18
    sujin190  
       2022-03-01 22:30:10 +08:00 via Android
    每个实例将近 40G 内存,如果是模块占用内存多那真是好奇你这些 module 都用来干嘛了,几十 G 的代码文件啊,再说 python 的模块缓存好像是按代码文件纬度来的吧,c 模块应该是 so 级别的,你确定不是你 module 直接引用加载数据了,代码文件这么大,有点不科学
    009694
        19
    009694  
       2022-03-01 22:55:03 +08:00 via iPhone
    fork 一个新进程去加载和计算你需要的动态库 用完即丢 。 动态需求要有动态的思路
    ipwx
        20
    ipwx  
       2022-03-01 23:13:52 +08:00
    @iyaozhen 来自 php 时代的 trick ( php-fpm ):后台进程负责 import cache ,过一段时间就杀死。这样就能从头再来了。
    ipwx
        21
    ipwx  
       2022-03-01 23:15:03 +08:00   1
    @sujin190 我这里也有个类似的需求,Python 在线生成 .so 然后 import 做计算的。.so 是根据上传的文本产生 C++ 代码然后编译出来 python 模块。。。所以用了个 subprocess
    24owls
        22
    24owls  
       2022-03-01 23:23:24 +08:00
    看了 #3 给的问题,import 进来的 module 无论如何也没法清除干净,那么想要清除干净就只能从一开始就不按照 importlib 默认的的机制来加载 module

    #8 提到的避免 import 可能是添加一个 meta hook 吧,加载需要以后清除的 module 时,通过读取代码文件后手动 exec 来加载
    ChrisFreeMan
        23
    ChrisFreeMan  
       2022-03-02 00:42:04 +08:00
    这种规模的内存占用,不知道这辈子能不能遇上一次,我就围观好了
    lolizeppelin
        24
    lolizeppelin  
       2022-03-02 09:27:18 +08:00
    围观 好奇怎么能让代码这么大....

    代码里塞数据了?
    ruanimal
        25
    ruanimal  
       2022-03-02 10:15:42 +08:00
    @lolizeppelin 估计是模型文件之类的
    iyaozhen
        26
    iyaozhen  
    OP
       2022-03-02 16:24:55 +08:00
    @24owls 嗯嗯 我都试试
    iyaozhen
        27
    iyaozhen  
    OP
       2022-03-02 16:25:30 +08:00
    @ChrisFreeMan 这不是因为使用问题嘛 正常哪需要这么多
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1075 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 33ms UTC 23:07 PVG 07:07 LAX 16:07 JFK 19:07
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86