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

2022-03-01 15:27:27 +08:00
 iyaozhen
有个业务需要动态加载很多本地文件( 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
4270 次点击
所在节点    Python
27 条回复
keepeye
2022-03-01 15:39:19 +08:00
帮顶,试试 importlib.invalidate_caches() 不知道是不是用来清缓存的
iyaozhen
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 implements 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
2022-03-01 15:54:11 +08:00
https://stackoverflow.com/questions/3105801/unload-a-module-in-python

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

------

BTW, builder 这个服务不用太管它,,偶尔还会有内存泄露问题 :-)
joApioVVx4M4X6Rf
2022-03-01 20:32:34 +08:00
楼主解决了吗
alphanow
2022-03-01 21:39:50 +08:00
sys.modules 只是一个 Python 引用列表,真正的对象是在堆记得,除了 Python 代码本身,底层的 C 代码有时也会对其存在引用。所以直接删除里面的条目可能是不起作用的。
所以有两个可能的解决方案:
用文件读取处理的方式生成一个对象,避免 import
开一个独立的 process 处理数据,用完直接干掉
iyaozhen
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
2022-03-01 21:48:54 +08:00
700G 内存,羡慕
iyaozhen
2022-03-01 21:52:53 +08:00
@zhengxiaowai 哈哈哈,老哥 你这留下的坑,请打开飞书交流

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

「开一个独立的 process 处理数据,用完直接干掉」改成多进程模型,这倒好像可以,但其实就用不上缓存了,不知道性能如何
Cooky
2022-03-01 22:01:22 +08:00
整个多进程,让子进程处理,处理完了关了重开?
iyaozhen
2022-03-01 22:02:01 +08:00
@imn1 运维已经要从我工资里面扣了 (开玩笑
iyaozhen
2022-03-01 22:02:57 +08:00
@Cooky 我试试
flynaj
2022-03-01 22:13:55 +08:00
要性能你上 golang,python,,
sujin190
2022-03-01 22:30:10 +08:00
每个实例将近 40G 内存,如果是模块占用内存多那真是好奇你这些 module 都用来干嘛了,几十 G 的代码文件啊,再说 python 的模块缓存好像是按代码文件纬度来的吧,c 模块应该是 so 级别的,你确定不是你 module 直接引用加载数据了,代码文件这么大,有点不科学
009694
2022-03-01 22:55:03 +08:00
fork 一个新进程去加载和计算你需要的动态库 用完即丢 。 动态需求要有动态的思路
ipwx
2022-03-01 23:13:52 +08:00
@iyaozhen 来自 php 时代的 trick ( php-fpm ):后台进程负责 import cache ,过一段时间就杀死。这样就能从头再来了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/837192

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX