Django 内存分析:逐个对象剖析运行中应用的内存占用(使用 OpenResty XRay)
要剖析一个 Django 应用的内存使用,只需把 OpenResty XRay 挂接到正在运行的、未经修改的 Python 进程上——无需改动任何代码——它就会生成一张 Python GC 对象内存分布火焰图,精确展示内存究竟消耗在哪里,细到某个模块的缓存字典这样的单个对象。本教程将一步步演示如何对一个未经修改、占用 86 MB 内存的 Django 进程做这样的分析。借助 OpenResty XRay 自动推导出的详细数据引用路径,您可以细致地检查任何一个 Python 对象,比如某个字符串或字典。
用 ps 找出内存占用高的 Django 进程
运行 ps aux | grep python3 列出所有 Python 进程。其中有一个 python3 manage.py runserver 进程格外显眼,占用了约 86 MB 的常驻内存(RSS)——而且它就是 Linux 发行版自带的标准 /bin/python3 二进制文件,未经任何修改。
用引导式分析剖析 Django 应用的内存使用
打开 OpenResty XRay 的 Web 控制台,确认选中的是正确的机器,然后进入 Guided Analysis(引导式分析)页面,在这里可以从 OpenResty XRay 能够诊断的问题类型中进行选择。
选择 High memory usage(高内存占用),再选中这个 Python Django 应用以及 ps 标记出的那个子进程(也就是占用 84 MB RSS 的那个),保持 Python 和 C/C++ 两个语言级别都选中,并使用默认的 300 秒上限开始分析。经过一两轮分析后,OpenResty XRay 会自动生成一份报告。在 Python-Land(Python 层)下,它给出了 GC 对象内存分布中排名第一的最热数据引用路径:一个占用 38.27 MB 的 dict,通过解释器的 .modules 到达。
这条数据引用路径告诉我们,解释器加载的 Python 模块一共占用了 38MB 的内存。
点击 “More” 查看细节信息。
这条数据引用路径是从这个 Python GC 对象内存分布火焰图中自动推导出来的。
下面是对当前问题更详细的解释和建议。
它提到了 .modules。
并提到它包含了所有与 Python 模块相关的内容。
接下来,我们放大火焰图,找出哪些 Python 模块占了较多内存。
可以看到这里有 1521 个 Python 模块被合并在一起。其中每一个模块都只占用了不到 1% 的内存。
这个模块占用了较多内存。我们可以点击它进行放大,查看更多的细节。
这里我们可以看到 openpyxl.utils.cell 模块占用了超过 2.6MB 的内存。openpyxl 是一个处理 Excel 文件的 Python 库。
复制模块名。
现在打开终端,使用 find 命令,在项目目录中搜索这个模块对应的源代码文件。项目目录是 Python 模块安装路径的根目录。
粘贴我们刚刚复制的模块名。
这里我们把模块名中的圆点当作 grep 命令的通配符使用。
复制这条完整的文件路径。
使用 vim 编辑器打开源文件,查看这个文件里的 Python 实现代码。您可以使用任何您喜欢的编辑器。
返回火焰图。
openpyxl 模块定义了两个字典,_COL_STRING_CACHE 和 _STRING_COL_CACHE。它们用于在 Excel 单元格坐标和列名之间进行转换。这两个变量占用了较多的内存。
复制第一个变量的名字。
然后搜索 _COL_STRING_CACHE,就可以找到这个对象。我们还可以找到这个文件中所有使用这个变量的地方。
_STRING_COL_CACHE 变量也可以通过同样的方法找到。
我们还可以用类似的方法分析火焰图上其他占用内存较多的模块。
比如说 linecache 模块。这是一个缓存文件内容的 Python 标准模块。它占用了 648KB 的内存。
它有一个叫做 cache 的变量,使用了很多内存。
Python 使用 Libc 分配器和 mmap 系统调用来分配内存。报告显示,Libc 分配器使用了 18.7MB 的内存。
用 Insights 自动监控 Django 内存使用
OpenResty XRay 还可以自动监控在线进程并展示分析报告。在 Insights 页面上,您可以找到以日和周为周期的报告——它们给出的内存引用路径和分布与前面相同,而无需手动运行引导式分析。当然,引导式分析在应用的开发和演示场景中仍然很有用。
常见问题
如何在不改动代码的情况下剖析 Django 应用的内存使用?
把 OpenResty XRay 挂接到已经在运行的、未经修改的 Python 进程上,然后启动一次引导式分析。它会实时读取该进程,因此您无需编辑、插桩或重新部署 Django 应用。分析结果是一份报告和一张火焰图,展示内存去了哪里,细到单个 Python 对象。
Django 应用中哪个 Python 模块占用内存最多?
放大 GC 对象内存分布火焰图,即可按大小对各模块排序。在本例中,openpyxl.utils.cell 模块在其 _COL_STRING_CACHE 和 _STRING_COL_CACHE 两个字典中占用了超过 2.6 MB,标准模块 linecache 则在其缓存文件内容的 cache 字典中保留了 648 KB。
什么是 Python GC 对象内存分布火焰图?
这是一种由 OpenResty 发明的火焰图,它按照每个 Python 垃圾回收对象所占的内存大小、以及它所在的数据引用路径来展开排列。OpenResty XRay 会自动生成并解释这张图,因此您可以把一个字符串或字典追溯回保留它的那个模块。
为什么一个不大的 Django 进程会占用这么多内存?
其中很大一部分来自解释器本身:已加载 Python 模块的 .modules 注册表占用了 38 MB(仅 1521 个模块对象本身就合计 13.7 MB),而各个模块级别的缓存——比如 openpyxl 的缓存字典——又在此基础上叠加了更多。火焰图会把每一 MB 都归因到某个具体对象,让您看清哪些内存值得削减。
关于 OpenResty XRay
OpenResty XRay 是一个动态追踪产品,它可以自动分析运行中的应用程序,以解决性能问题、行为问题和安全漏洞,并提供可行的建议。在底层实现上,OpenResty XRay 由我们的 Y 语言驱动,可以在不同环境下支持多种不同的运行时,如 Stap+、eBPF+、GDB 和 ODB。
关于作者
章亦春是开源 OpenResty® 项目创始人兼 OpenResty Inc. 公司 CEO 和创始人。
章亦春(Github ID: agentzh),生于中国江苏,现定居美国湾区。他是中国早期开源技术和文化的倡导者和领军人物,曾供职于多家国际知名的高科技企业,如 Cloudflare、雅虎、阿里巴巴, 是 “边缘计算“、”动态追踪 “和 “机器编程 “的先驱,拥有超过 22 年的编程及 16 年的开源经验。作为拥有超过 4000 万全球域名用户的开源项目的领导者。他基于其 OpenResty® 开源项目打造的高科技企业 OpenResty Inc. 位于美国硅谷中心。其主打的两个产品 OpenResty XRay(利用动态追踪技术的非侵入式的故障剖析和排除工具)和 OpenResty Edge(最适合微服务和分布式流量的全能型网关软件),广受全球众多上市及大型企业青睐。在 OpenResty 以外,章亦春为多个开源项目贡献了累计超过百万行代码,其中包括,Linux 内核、Nginx、LuaJIT、GDB、SystemTap、LLVM、Perl 等,并编写过 60 多个开源软件库。
关注我们
如果您喜欢本文,欢迎关注我们 OpenResty Inc. 公司的博客网站 。也欢迎扫码关注我们的微信公众号:
翻译
我们提供了英文版原文和中译版(本文)。我们也欢迎读者提供其他语言的翻译版本,只要是全文翻译不带省略,我们都将会考虑采用,非常感谢!














































