溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python垃圾回收及Linux?Fork實例分析

發(fā)布時間:2022-01-13 10:43:58 來源:億速云 閱讀:123 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了Python垃圾回收及Linux Fork實例分析的相關(guān)知識,內(nèi)容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Python垃圾回收及Linux Fork實例分析文章都會有所收獲,下面我們一起來看看吧。

前言:

在口袋助理看到了其他部門的同事針對Python2內(nèi)存占用做的一點優(yōu)化工作,自己比較感興趣,遂記錄下。

1.Linux fork簡介

fork是Linux提供的創(chuàng)建子進程的系統(tǒng)調(diào)用。為了優(yōu)化創(chuàng)建進程速度,Linux內(nèi)核使用了Copy-on-Write的方式去創(chuàng)建進程,所謂Copy-on-Write是指執(zhí)行fork之后,
內(nèi)核并不立即給子進程分配物理內(nèi)存空間,而是讓子進程的虛內(nèi)存映射到父進程的物理內(nèi)存。僅僅當子進程向地址空間中執(zhí)行寫入操作時,才給它分配一段物理內(nèi)存。
通過這種方式既優(yōu)化了進程創(chuàng)建的時間,又減少了子進程的內(nèi)存占用。

1.Copy-On-Write策略增加Python多進程內(nèi)存占用的原因

Python GC采用引用技術(shù)的方式去管理對每個對象的引用,每一個被GC跟蹤的對象會由一個PyGC_Head的結(jié)構(gòu)體去表示。如下所示,其中gc_refs就是每個對象的引用計數(shù)值,
當我們在子進程中讀取父進程創(chuàng)建的對象的時候,就會導(dǎo)致子進程的虛地址空間中的gc_refs加1,從而觸發(fā)了內(nèi)核的缺頁中斷,這是內(nèi)核就會給子進程創(chuàng)建新的物理內(nèi)存。
僅僅是簡單的讀取操作就會導(dǎo)致新的內(nèi)存空間產(chǎn)生。

/* 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]。

Python垃圾回收及Linux?Fork實例分析

freeze用于將GC追蹤的所有對象都移動到永生代(permanent generation),之后垃圾回收會忽略這些被設(shè)置為永生代的對象。

實際使用中,我們可以在父進程中執(zhí)行freeze函數(shù),然后子進程中使用和父進程共享的對象,這樣對象的引用技術(shù)就不會增加,從而避免了COW的發(fā)生。

python2的解決方法:

  • (1) 針對Python2,我們可以簡單的把Python3的相關(guān)函數(shù)移植過來

  • (2) 使用multiprocessing.Array去共享數(shù)據(jù)。Array會從共享內(nèi)存中取一段取存儲數(shù)據(jù),并不會增加引用技術(shù)值,從而觸發(fā)COW。

實現(xiàn)方面,Array使用Posix共享內(nèi)存 + mmap去實現(xiàn)。[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()

關(guān)于“Python垃圾回收及Linux Fork實例分析”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Python垃圾回收及Linux Fork實例分析”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI