Python?tracemalloc跟蹤內(nèi)存分配問題
Python tracemalloc跟蹤內(nèi)存分配
tracemalloc 模塊是一個用于對 python 已申請的內(nèi)存塊進行debug的工具。
它能提供以下信息:
- 定位對象分配內(nèi)存的位置
- 按文件、按行統(tǒng)計python的內(nèi)存塊分配情況: 總大小、塊的數(shù)量以及塊平均大小。
- 對比兩個內(nèi)存快照的差異,以便排查內(nèi)存泄漏
顯示前10項
顯示內(nèi)存分配最多的10個文件:
import tracemalloc tracemalloc.start() # --- 業(yè)務(wù)代碼 start --- n = 10000000 s = 0 for i in range(1, n): s *= i # --- 業(yè)務(wù)代碼 end --- snapshot = tracemalloc.take_snapshot() # 內(nèi)存攝像 top_stats = snapshot.statistics('lineno') # 內(nèi)存占用數(shù)據(jù)獲取 print('[Top 10]') for stat in top_stats[:10]: # 打印占用內(nèi)存最大的10個子進程 print(stat) # [Top 10] # D:/MyPython/tracemalloc/demo.py:5: size=576 B, count=1, average=576 B # D:/MyPython/tracemalloc/demo.py:7: size=28 B, count=1, average=28 B
TOP1:代碼第五行占用內(nèi)存大小576B
計算差異
獲取兩個快照并顯示差異:
import tracemalloc tracemalloc.start() snapshot0 = tracemalloc.take_snapshot() # 第一張快照 # --- 業(yè)務(wù)代碼 start --- n = 10000000 s = 0 for i in range(1, n): s *= i # --- 業(yè)務(wù)代碼 end --- snapshot1 = tracemalloc.take_snapshot() # 第二張快照 top_stats = snapshot1.compare_to(snapshot0, 'lineno') # 快照對比 print('[Top 10 differences]') for stat in top_stats[:10]: print(stat) # [Top 10 differences] # D:/MyPython/tracemalloc/demo.py:27: size=576 B (+576 B), count=1 (+1), average=576 B # D:\Program Files\anaconda3\lib\tracemalloc.py:397: size=88 B (+88 B), count=2 (+2), average=44 B # D:\Program Files\anaconda3\lib\tracemalloc.py:534: size=48 B (+48 B), count=1 (+1), average=48 B # D:\Program Files\anaconda3\lib\tracemalloc.py:291: size=40 B (+40 B), count=1 (+1), average=40 B # D:/MyPython/tracemalloc/demo.py:31: size=28 B (+28 B), count=1 (+1), average=28 B
TOP1:代碼第27行占用內(nèi)存大小增加了576B
tracemalloc分析內(nèi)存使用情況與泄露
概述
python內(nèi)存管理是通過引用計數(shù)執(zhí)行的,如果指向某個對象的引用全部過期,那么受引用的對象就可以從內(nèi)存中清除,從而給其他數(shù)據(jù)騰出空間。
理論上講,python開發(fā)不用擔心程序如何分配和釋放內(nèi)存,因為python系統(tǒng)本身以及Cpython運行環(huán)境會自動處理這些問題。
但實際情況程序會因為沒有及時釋放不再需要引用的數(shù)據(jù)耗盡內(nèi)存。下面通過一些方法來看下內(nèi)存使用情況。
查看gc引用對象總數(shù)
下面是被測試代碼,這個代碼可以創(chuàng)建對象,在gc中產(chǎn)生引用對象。
import os class MyObject: def __init__(self): self.data = os.urandom(100) def get_data(): values = [] for _ in range(100): obj = MyObject() values.append(obj) return values def run(): deep_values = [] for _ in range(100): deep_values.append(get_data()) return
下面的代碼用來輸出當前gc引用對象的數(shù)量
import gc # 獲取運行前gc引用對象數(shù)量 found_objects = gc.get_objects() print('Before:', len(found_objects)) # 導(dǎo)入待測試模塊 import waste_memory # 運行待測試代碼的函數(shù) hold_reference = waste_memory.run() # 獲取運行代碼后gc引用對象數(shù)量 found_objects = gc.get_objects() print('After: ', len(found_objects)) for obj in found_objects[:5]: print(repr(obj)[:100]) print('...')
運行上面的代碼,下面是gc引用的對象總數(shù)。
Before: 28834 After: 28923
tracemalloc查看內(nèi)存分配情況
1.查看內(nèi)存分配情況
上面只輸出了gc的總數(shù),對于分析內(nèi)存分配情況沒有太多的指導(dǎo)意義,tracemalloc模塊能夠追溯到分配它的位置,因此我們可以在之前模塊前后對內(nèi)存使用情況做個快照,分析兩個快照之間的區(qū)別。
下面是被測試代碼
import tracemalloc tracemalloc.start(10) # Set stack depth time1 = tracemalloc.take_snapshot() # Before snapshot import waste_memory x = waste_memory.run() # Usage to debug time2 = tracemalloc.take_snapshot() # After snapshot stats = time2.compare_to(time1, 'lineno') # Compare snapshots for stat in stats[:3]: print(stat)
運行上面的代碼,從結(jié)果中可以看出,每一條記錄都有size與count指標,用來表示這行代碼所分配的對象占用多少內(nèi)存,以及對象的數(shù)量。通過對比就能發(fā)現(xiàn)占用內(nèi)存較多的對象是由那幾行代碼分配的。
/waste_memory.py:11: size=5120 B (+5120 B), count=80 (+80), average=64 B /waste_memory.py:14: size=4424 B (+4424 B), count=79 (+79), average=56 B /waste_memory.py:9: size=1704 B (+1704 B), count=8 (+8), average=213 B
2.查看棧信息
tracemalloc還可以打印棧的追蹤信息,下面把程序中分配內(nèi)存最多的那行代碼所對應(yīng)的棧追蹤信息打印出來,看看程序是沿著哪條路徑觸發(fā)這行代碼的。
import tracemalloc tracemalloc.start(10) time1 = tracemalloc.take_snapshot() import waste_memory x = waste_memory.run() time2 = tracemalloc.take_snapshot() stats = time2.compare_to(time1, 'traceback') top = stats[0] print('Biggest offender is:') # 打印棧信息 print('\n'.join(top.traceback.format()))
運行上面的代碼
Biggest offender is: File "/with_trace.py", line 14 x = waste_memory.run() File "/waste_memory.py", line 23 deep_values.append(get_data()) File "/waste_memory.py", line 16 obj = MyObject() File "/waste_memory.py", line 11 self.data = os.urandom(100)
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python使用execute_script模擬鼠標滾動、鼠標點擊等示例
文章介紹了Python使用Selenium執(zhí)行JavaScript來繞過網(wǎng)站對爬蟲的限制,包括模擬點擊、攔截彈出窗口、創(chuàng)建并派發(fā)點擊事件、模擬鼠標懸停后點擊和滾動到元素并點擊等方法2025-02-02python 利用已有Ner模型進行數(shù)據(jù)清洗合并代碼
今天小編就為大家分享一篇python 利用已有Ner模型進行數(shù)據(jù)清洗合并代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12python使用QQ郵箱實現(xiàn)自動發(fā)送郵件
這篇文章主要為大家詳細介紹了python使用QQ郵箱實現(xiàn)自動發(fā)送郵件,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-06-06Python中random.shuffle()函數(shù)用法代碼案例
random.shuffle方法,對元素進行重新排序,打亂原有的順序,返回一個隨機序列,該方法的作用類似洗牌,本文重點給大家介紹Python中random.shuffle()函數(shù)用法代碼案例,感興趣的朋友跟隨小編一起看看吧2022-11-11500行代碼使用python寫個微信小游戲飛機大戰(zhàn)游戲
這篇文章主要介紹了500行代碼使用python寫個微信小游戲飛機大戰(zhàn)游戲,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10在Python中使用defaultdict初始化字典以及應(yīng)用方法
今天小編就為大家分享一篇在Python中使用defaultdict初始化字典以及應(yīng)用方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10python實現(xiàn)切割url得到域名、協(xié)議、主機名等各個字段的例子
今天小編就為大家分享一篇python實現(xiàn)切割url得到域名、協(xié)議、主機名等各個字段的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07Python中print和return的作用及區(qū)別解析
print的作用是輸出數(shù)據(jù)到控制端,就是打印在你能看到的界面上。這篇文章給大家介紹Python中print和return的作用及區(qū)別解析,感興趣的朋友跟隨小編一起看看吧2019-05-05