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. 公司的部落格網站 。也歡迎掃碼關注我們的微信公眾號:
翻譯
我們提供了英文版原文和中譯版(本文)。我們也歡迎讀者提供其他語言的翻譯版本,只要是全文翻譯不帶省略,我們都將會考慮採用,非常感謝!













































