一文詳解Python程序退出時的內(nèi)存管理機制
一、Python 內(nèi)存管理基礎(chǔ)
1.1 Python 內(nèi)存分配層次
Python 的內(nèi)存管理分為幾個層次:
- Python 對象層:通過 Python 內(nèi)存管理器分配
- Python 內(nèi)存管理器層:使用 Python 的私有內(nèi)存分配器
- 操作系統(tǒng)層:調(diào)用 C 的 malloc/free 或 mmap/munmap

1.2 引用計數(shù)與垃圾回收
Python 主要使用兩種內(nèi)存管理機制:
1.引用計數(shù):
- 每個對象維護一個引用計數(shù)
- 當(dāng)計數(shù)歸零時立即釋放內(nèi)存
- 無法解決循環(huán)引用問題
2.分代垃圾回收(GC):
- 專門處理循環(huán)引用
- 分為三代(0,1,2)
- 按不同頻率檢查各代對象
二、程序退出時的內(nèi)存釋放行為
2.1 常規(guī)情況下的內(nèi)存釋放
當(dāng) Python 程序正常退出時:
1.Python 解釋器會執(zhí)行清理操作:
- 調(diào)用各模塊的
__del__方法 - 釋放所有 Python 對象
- 關(guān)閉打開的文件等資源
- 釋放內(nèi)存分配器管理的所有內(nèi)存
2.操作系統(tǒng)回收所有進程資源:
- 現(xiàn)代操作系統(tǒng)會在進程終止時回收其所有資源
- 包括內(nèi)存、文件描述符、網(wǎng)絡(luò)連接等
- 這是操作系統(tǒng)級別的保證
2.2 驗證內(nèi)存釋放的代碼示例
可以通過以下代碼驗證內(nèi)存釋放情況:
import os
import psutil # 需要安裝: pip install psutil
def show_memory():
process = psutil.Process(os.getpid())
print(f"內(nèi)存使用: {process.memory_info().rss/1024/1024:.2f} MB")
# 分配大量內(nèi)存
big_list = [x for x in range(10_000_000)]
show_memory()
# 刪除引用
del big_list
show_memory()
運行結(jié)果會顯示內(nèi)存被正確釋放。
2.3 特殊情況下的內(nèi)存行為
雖然大多數(shù)情況下內(nèi)存會被釋放,但存在一些特殊情況:
1.擴展模塊的內(nèi)存泄漏:
- C 擴展模塊可能不正確地管理內(nèi)存
- 特別是那些直接使用 malloc/free 的模塊
2.全局/靜態(tài)變量的內(nèi)存:
某些 C 擴展中的全局變量可能持續(xù)存在
3.共享內(nèi)存:
- 使用 multiprocessing 的共享內(nèi)存
- mmap 映射的內(nèi)存區(qū)域
三、可能的內(nèi)存泄漏場景
3.1 Python 層面的內(nèi)存泄漏
雖然 Python 有自動內(nèi)存管理,但仍可能發(fā)生泄漏:
1.循環(huán)引用與 __del__ 方法:
class Node:
def __init__(self):
self.parent = None
self.children = []
def __del__(self):
print("Node deleted")
# 創(chuàng)建循環(huán)引用
parent = Node()
child = Node()
parent.children.append(child)
child.parent = parent
2.全局變量持續(xù)引用:
_cache = {}
def process_data(data):
_cache[data.id] = data # 數(shù)據(jù)永遠不被釋放
3.2 擴展模塊的內(nèi)存泄漏
C 擴展模塊可能造成更嚴(yán)重的內(nèi)存泄漏:
// 錯誤的C擴展示例:內(nèi)存泄漏
static PyObject* leak_memory(PyObject* self, PyObject* args) {
void* memory = malloc(1024); // 分配內(nèi)存
// 忘記free(memory)
Py_RETURN_NONE;
}
四、確保完全釋放內(nèi)存的最佳實踐
4.1 顯式資源清理
使用上下文管理器:
with open('file.txt') as f:
content = f.read()
# 文件自動關(guān)閉
手動清理循環(huán)引用:
def clear_circular_refs():
global parent, child
parent.children = []
child.parent = None
del parent, child
4.2 監(jiān)控內(nèi)存使用
使用內(nèi)存分析工具:
tracemalloc:跟蹤內(nèi)存分配objgraph:可視化對象引用memory_profiler:逐行分析內(nèi)存使用
示例使用 tracemalloc:
import tracemalloc
tracemalloc.start()
# 執(zhí)行可能泄漏內(nèi)存的代碼
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
print(stat)
五、程序退出方式的影響
不同的退出方式對內(nèi)存釋放有不同影響:
| 退出方式 | 內(nèi)存釋放情況 | 資源清理完整性 |
|---|---|---|
| 正常退出(sys.exit(0)) | 完全釋放 | 完整 |
| 異常退出(sys.exit(1)) | 完全釋放 | 完整 |
| 強制終止(kill -9) | 操作系統(tǒng)回收 | 可能不完整 |
| 子進程終止 | 取決于子進程實現(xiàn) | 可能不完整 |
六、底層原理深入
6.1 Python 解釋器退出流程
- 調(diào)用
Py_Finalize()開始清理 - 執(zhí)行所有模塊的
__del__方法 - 清除所有 Python 對象
- 釋放類型系統(tǒng)和內(nèi)存分配器
- 調(diào)用
atexit注冊的函數(shù) - 返回控制權(quán)給操作系統(tǒng)
6.2 操作系統(tǒng)層面的進程終止
當(dāng)進程終止時,現(xiàn)代操作系統(tǒng)會:
- 釋放進程的所有內(nèi)存頁
- 關(guān)閉所有文件描述符
- 釋放其他內(nèi)核資源
- 移除進程表項
七、特殊情況處理
7.1 共享內(nèi)存的特殊情況
使用 multiprocessing 的共享內(nèi)存:
from multiprocessing import shared_memory shm = shared_memory.SharedMemory(create=True, size=1024) # 程序退出后共享內(nèi)存塊可能仍然存在 shm.unlink() # 必須顯式unlink才能完全釋放
7.2 內(nèi)存映射文件
使用 mmap 的內(nèi)存:
import mmap
with open("data.file", "r+b") as f:
mm = mmap.mmap(f.fileno(), 0)
# 使用內(nèi)存映射...
mm.close() # 必須顯式關(guān)閉
八、總結(jié)與最佳實踐
8.1 關(guān)鍵結(jié)論
正常情況下:Python 程序退出時會釋放所有分配的內(nèi)存
例外情況:
- 有 bug 的 C 擴展可能泄漏內(nèi)存
- 共享內(nèi)存和內(nèi)存映射需要特殊處理
- 某些系統(tǒng)資源可能需要顯式釋放
8.2 最佳實踐建議
對于常規(guī) Python 代碼:
- 依賴 Python 的自動內(nèi)存管理
- 注意避免不必要的全局變量
- 小心處理循環(huán)引用
對于資源密集型應(yīng)用:
def cleanup():
# 顯式釋放資源
global resource
resource.release()
del resource
import atexit
atexit.register(cleanup)
對于使用擴展模塊的情況:
- 選擇質(zhì)量有保障的擴展
- 監(jiān)控內(nèi)存使用情況
- 考慮使用隔離進程運行不可靠代碼
開發(fā)階段建議:
- 使用內(nèi)存分析工具定期檢查
- 為資源類對象實現(xiàn)上下文管理器
- 編寫單元測試驗證資源釋放
Python 的內(nèi)存管理雖然大多數(shù)時候是自動且可靠的,但理解其底層機制和邊界情況對于開發(fā)健壯、高效的應(yīng)用程序至關(guān)重要。特別是在長期運行的服務(wù)和資源密集型應(yīng)用中,合理的內(nèi)存管理實踐可以避免許多難以調(diào)試的問題。
到此這篇關(guān)于一文詳解Python程序退出時的內(nèi)存管理機制的文章就介紹到這了,更多相關(guān)Python內(nèi)存管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Pyside6開發(fā)使用Qt?Designer的示例代碼
本文主要介紹了Pyside6開發(fā)使用Qt?Designer的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-05-05
Python使用lxml庫和Xpath提取網(wǎng)頁數(shù)據(jù)的完整指南與最佳實戰(zhàn)
本文介紹使用Python的lxml庫和Xpath提取網(wǎng)頁數(shù)據(jù),涵蓋lxml庫基礎(chǔ)用法、代碼實戰(zhàn),還提及處理動態(tài)加載和命名空間等進階用法,同時闡述了爬蟲的錯誤處理、數(shù)據(jù)存儲、部署、監(jiān)控等內(nèi)容,以及后續(xù)的數(shù)據(jù)處理、分析、可視化和報告撰寫等步驟,需要的朋友可以參考下2025-11-11
動態(tài)設(shè)置django的model field的默認值操作步驟
這篇文章主要介紹了動態(tài)設(shè)置django的model field的默認值操作步驟,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
python中concurrent.futures的具體使用
concurrent.futures是Python標(biāo)準(zhǔn)庫的一部分,提供了ThreadPoolExecutor和ProcessPoolExecutor兩種執(zhí)行器,用于管理線程池和進程池,通過這些執(zhí)行器,可以簡化多線程和多進程任務(wù)的管理,提高程序執(zhí)行效率2024-09-09
舉例簡單講解Python中的數(shù)據(jù)存儲模塊shelve的用法
這篇文章主要介紹了舉例簡單講解Python中的數(shù)據(jù)存儲模塊shelve的用法,shelveshelve模塊與pickle模塊的功能相近,比pickle用起來更為簡單,需要的朋友可以參考下2016-03-03
python提示No module named images的解決方法
這篇文章主要介紹了python提示No module named images的解決方法,是Python程序設(shè)計中經(jīng)常遇到的問題,本文給出了具有針對性的解決方法,需要的朋友可以參考下2014-09-09
Python使用Pandas和Matplotlib按中值對箱形圖進行排序
箱形圖是可視化數(shù)據(jù)分布的強大工具,因為它們提供了對數(shù)據(jù)集內(nèi)的散布、四分位數(shù)和離群值的洞察,在本文中,我們將探索如何在Python中使用Pandas和Matplotlib按中值對箱形圖進行排序,需要的朋友可以參考下2025-04-04

