Python垃圾回收及Linux Fork
前言:
在口袋助理看到瞭其他部門的同事針對Python2
內存占用做的一點優化工作,自己比較感興趣,遂記錄下。
1.Linux fork簡介
fork是Linux提供的創建子進程的系統調用。為瞭優化創建進程速度,Linux內核使用瞭Copy-on-Write
的方式去創建進程,所謂Copy-on-Write
是指執行fork之後,
內核並不立即給子進程分配物理內存空間,而是讓子進程的虛內存映射到父進程的物理內存。僅僅當子進程向地址空間中執行寫入操作時,才給它分配一段物理內存。
通過這種方式既優化瞭進程創建的時間,又減少瞭子進程的內存占用。
1.Copy-On-Write策略增加Python多進程內存占用的原因
Python GC采用引用技術的方式去管理對每個對象的引用,每一個被GC跟蹤的對象會由一個PyGC_Head的結構體去表示。如下所示,其中gc_refs
就是每個對象的引用計數值,
當我們在子進程中讀取父進程創建的對象的時候,就會導致子進程的虛地址空間中的gc_refs
加1,從而觸發瞭內核的缺頁中斷,這是內核就會給子進程創建新的物理內存。
僅僅是簡單的讀取操作就會導致新的內存空間產生。
/* GC information is stored BEFORE the object structure. */ typedef union _gc_head { struct { union _gc_head *gc_next; union _gc_head *gc_prev; Py_ssize_t gc_refs; } gc; long double dummy; /* force worst-case alignment */ } PyGC_Head;
3.解決辦法
python3的解決方法:
針對這個問題,Python3.7增加瞭三組API(有instagram團體提交的)[1]。
freeze
用於將GC追蹤的所有對象都移動到永生代(permanent generation)
,之後垃圾回收會忽略這些被設置為永生代的對象。
實際使用中,我們可以在父進程中執行freeze
函數,然後子進程中使用和父進程共享的對象,這樣對象的引用技術就不會增加,從而避免瞭COW的發生。
python2的解決方法:
- (1) 針對Python2,我們可以簡單的把Python3的相關函數移植過來
- (2) 使用multiprocessing.Array去共享數據。Array會從共享內存中取一段取存儲數據,並不會增加引用技術值,從而觸發COW。
實現方面,Array使用Posix共享內存 + mmap去實現。[3]
#!/usr/bin/env python # coding=utf-8 from multiprocessing import Array import os import sys def foo(): shared_cache = Array('i', range(0, 100), lock=False) pid = os.fork() if pid > 0: print("parent:", sys.getrefcount(shared_cache)) elif pid == 0: print("child:", sys.getrefcount(shared_cache)) foo()
到此這篇關於Python垃圾回收及Linux Fork的文章就介紹到這瞭,更多相關Python垃圾回收及Linux Fork內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
參考:
1.https://instagram-engineering.com/copy-on-write-friendly-python-garbage-collection-ad6ed5233ddf
2.https://llvllatrix.wordpress.com/2016/02/19/python-vs-copy-on-write/
3.https://github.com/python/cpython/blob/main/Lib/multiprocessing/shared_memory.py