Python中內(nèi)存管理機制與優(yōu)化技巧分享
還記得你第一次寫 Python 代碼的時候嗎?那種隨意創(chuàng)建變量、毫無節(jié)制地 new 一個又一個對象的快感,簡直讓人沉迷!可是,當代碼運行變慢、內(nèi)存占用飆升時,你才會意識到:
“誒?怎么 Python 還會吃這么多內(nèi)存?!”
今天,我們就來聊聊 Python 的內(nèi)存管理,幫你寫出更高效、優(yōu)化內(nèi)存占用的 Python 代碼!
1. Python 的內(nèi)存管理機制
Python 內(nèi)部使用 引用計數(shù)(Reference Counting) 和 垃圾回收(Garbage Collection, GC) 機制來管理內(nèi)存。
1.1 引用計數(shù)
每個對象都有一個“計數(shù)器”,記錄它被多少個變量引用。一旦引用計數(shù)歸零,Python 立刻釋放這個對象的內(nèi)存。
import sys a = [] # 創(chuàng)建一個列表對象 print(sys.getrefcount(a)) # 輸出 2(因為 sys.getrefcount() 也會額外增加一次引用) b = a # 變量 b 也指向同一個列表 print(sys.getrefcount(a)) # 輸出 3 del a print(sys.getrefcount(b)) # 輸出 2 del b # 引用計數(shù)歸零,內(nèi)存被釋放
注意:sys.getrefcount()
的結(jié)果比你想象的多 1,因為它本身也會創(chuàng)建一個臨時引用!
1.2 垃圾回收(GC)
Python 采用 分代回收,對象被分成三代:新生代、中生代、老生代。垃圾回收主要針對循環(huán)引用的情況。
import gc class A: def __init__(self): self.ref = None obj1 = A() obj2 = A() obj1.ref = obj2 obj2.ref = obj1 # 形成循環(huán)引用 del obj1, obj2 # 引用計數(shù)沒有歸零,Python 需要 GC 來清理 gc.collect() # 手動觸發(fā)垃圾回收
2. Python 內(nèi)存優(yōu)化技巧
2.1 使用 __slots__限制對象屬性
默認情況下,Python 的對象使用 動態(tài)字典(__dict__
) 存儲屬性,占用大量內(nèi)存。如果你的類屬性是固定的,可以用 __slots__
優(yōu)化,之前花姐在其它文章中提到過。
class NormalClass: pass class SlotClass: __slots__ = ['name', 'age'] # 僅允許 name 和 age 兩個屬性 obj1 = NormalClass() obj1.name = "花姐" obj1.age = 18 obj1.gender = "女" # 允許動態(tài)添加新屬性 obj2 = SlotClass() obj2.name = "花姐" obj2.age = 18 # obj2.gender = "女" # ? AttributeError: 'SlotClass' object has no attribute 'gender'
__slots__
會讓 Python 不再為對象創(chuàng)建 __dict__
,從而減少內(nèi)存占用。
2.2 避免不必要的臨時變量
Python 解釋器會緩存一些常見的對象,例如 小整數(shù)(-5
到 256
在 Python 3.9 及以前的版本),以及部分 短字符串。
但在 Python 3.10+ 之后,整數(shù)的緩存范圍 可能更大,具體行為依賴于 Python 實現(xiàn)。
# 可能被緩存(具體范圍取決于 Python 版本) a = 256 b = 256 print(a is b) # True # 可能不被緩存 a = 257 b = 257 print(a is b) # 3.9 以前通常 False,3.10+ 可能 True
結(jié)論:Python 會緩存小整數(shù),但具體范圍視 Python 版本而定,不建議過分依賴此特性!
2.3 使用生成器代替列表
如果你只需要 逐個獲取數(shù)據(jù),而不是一次性加載所有數(shù)據(jù),請用 生成器 代替列表。
# 占用大量內(nèi)存的方式 nums = [i for i in range(10**6)] # 更優(yōu)的方式(惰性加載) def num_generator(): for i in range(10**6): yield i gen = num_generator()
為什么? 生成器不會一次性把所有數(shù)據(jù)存入內(nèi)存,而是每次 yield
一個值,這樣可以大幅降低內(nèi)存占用!
2.4 使用 array 代替列表存儲大量數(shù)值
如果你需要存儲大量的數(shù)值,使用 array
模塊比 list
更節(jié)省內(nèi)存。
import array # 創(chuàng)建一個存儲 int 類型的數(shù)組,比列表更節(jié)省內(nèi)存 arr = array.array('i', range(10**6))
2.5 使用 deque 代替列表進行隊列操作
collections.deque
具有更高效的 頭部插入和刪除 操作,比 list
的 pop(0)
和 insert(0, x)
更優(yōu)。
from collections import deque dq = deque(range(10**6)) dq.appendleft(-1) # O(1) 復雜度 # 而 list.insert(0, -1) 是 O(n),在大規(guī)模數(shù)據(jù)下性能差距明顯
3. 釋放不用的內(nèi)存
手動釋放變量
Python 采用 自動垃圾回收,但如果你想主動釋放大對象,建議使用 del
并 調(diào)用 gc.collect()
。
import gc data = [i for i in range(10**6)] del data # 刪除變量 gc.collect() # 強制觸發(fā)垃圾回收
在大數(shù)據(jù)處理中,這個方法可以 顯著減少內(nèi)存占用!
總結(jié)
Python 采用 引用計數(shù) + 垃圾回收 來管理內(nèi)存。
使用 __slots__ 可以節(jié)省對象的 屬性存儲空間。
避免不必要的臨時變量,Python 會緩存小整數(shù),但范圍 依賴 Python 版本。
用生成器替代列表,節(jié)省內(nèi)存!
用 array 代替 list 存儲大量數(shù)值,提高內(nèi)存效率。
用 deque 代替 list 進行隊列操作,提高性能。
手動釋放大對象,使用 del + gc.collect() 及時清理。
到此這篇關(guān)于Python中內(nèi)存管理機制與優(yōu)化技巧分享的文章就介紹到這了,更多相關(guān)Python內(nèi)存管理與優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 使用xlsxwriter循環(huán)向excel中插入數(shù)據(jù)和圖片的操作
這篇文章主要介紹了python 使用xlsxwriter循環(huán)向excel中插入數(shù)據(jù)和圖片的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01python基于scrapy爬取京東筆記本電腦數(shù)據(jù)并進行簡單處理和分析
這篇文章主要介紹了python基于scrapy爬取京東筆記本電腦數(shù)據(jù)并進行簡單處理和分析的實例,幫助大家更好的理解和學習使用python。感興趣的朋友可以了解下2021-04-04Python使用wxPython和PyMuPDF提取PDF頁面指定頁數(shù)的內(nèi)容
在本篇博客中,我們將探討如何使用wxPython和PyMuPDF庫創(chuàng)建一個簡單的Bokeh應用程序,用于選擇PDF文件并提取指定頁面的內(nèi)容,并將提取的內(nèi)容顯示在文本框中,需要的朋友可以參考下2023-08-08Python實現(xiàn)掃描局域網(wǎng)活動ip(掃描在線電腦)
這篇文章主要介紹了Python實現(xiàn)掃描局域網(wǎng)活動ip(掃描在線電腦),本文直接給出實現(xiàn)代碼,需要的朋友可以參考下2015-04-04Python列表數(shù)據(jù)如何按區(qū)間分組統(tǒng)計各組個數(shù)
這篇文章主要介紹了Python列表數(shù)據(jù)如何按區(qū)間分組統(tǒng)計各組個數(shù),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07