1. 程式人生 > 實用技巧 >python記憶體管理機制詳解

python記憶體管理機制詳解

  • 本文根據記憶體管理機制,採用例項來驗證記憶體管理機制,並提出了自己對於python記憶體管理機制的一些見解。

1 By code

1.1 引用計數+垃圾回收機制

import sys
class Person:
    pass

p1 = Person()
p2 = p1
print('p1引用計數:', sys.getrefcount(p1))           # p1引用計數: 3
# del p2
# print('p1引用計數:', sys.getrefcount(p1))         # p2引用計數: 2

print(id(p1))                                       # 1901831032904
print(id(p2))                                       # 1901831032904

if p1 is p2:
    print('yes')                                    # yes

print(sys.getsizeof(p1))                            # 56 bites

1.2 分代回收

import sys
class Person:
    pass

class Dog:
    pass

p = Person()
d = Dog()
p.pet = d
d.master = p

print('p引用計數:', sys.getrefcount(p))           # p引用計數: 3
print('d引用計數:', sys.getrefcount(d))           # d引用計數: 3

del p
print('d引用計數:', sys.getrefcount(d))           # d引用計數: 3

2 Notice

2.1 引用計數

引用計數是程式語言總的一種記憶體管理技術,將資源的被引用次數儲存起來。

  • 資源:可以是物件,記憶體或磁碟空間等
  • def語句會刪除物件的一個引用,這將導致該引用指向的物件的引用計數減1
  • 任何追蹤或除錯程式會給物件增加一個額外引用,這會推遲該物件被回收的時間
  • 由於兩個或以上物件互相引用時,彼此引用計數不為0,造成迴圈應用而無法回收

2.2 標記-清除

  • 標記清除分為兩個階段,首先標記物件(垃圾檢測),然後清除垃圾(垃圾回收)
2.2.1 標記

在此階段,垃圾回收器會從跟物件開始遍歷。

每個可以從根物件訪問到的物件都會被新增標識,這個物件被標識為可到達物件,可到達物件不會被清除。

2.2.2 清除

在此階段,垃圾回收器會對堆記憶體從頭到尾進行線性遍歷

如果發現有物件沒有被標識為可到達物件,那麼就此物件記憶體回收

之後將原來標記為可到達物件的標識抹掉,以便進行下一次垃圾回收操作

2.2.3 標記清除存在的問題

標記-清除演算法的比較大的缺點就是垃圾收集後有可能會造成大量的記憶體碎片,其次由於需要遍歷的記憶體節點較多時,每次回收都將花費時間在遍歷節點過程中

2.2.4 注意事項
  • collector

垃圾收集器

  • mutator

指的是垃圾收集器之外的部分(比如當前的應用程式,可以直接被mutator直接訪問到的物件,一般指靜態---->全域性變數)

  • 可到達物件

所謂的可到達物件就是從根物件開始遍歷,可以訪問到的物件,也就是mutator(應用程式)正在使用的物件

2.3 分代回收

  • 由於巢狀引用無法被回收(分層,打標籤)
2.3.1 核心思想

分代是一種典型的以空間換時間的技術

這種思想簡單點說就是:物件存在時間越長,越可能不是垃圾,應該越少去收集

分代是解決記憶體遍歷需要太久時間而誕生的解決方案

2.3.2 分代回收的好處

分代回收可以減少標記-清除機制所帶來的額外操作

分代就是將回收物件分成數個代(青年代,中年代,老年代),每個代都是一個連結串列(集合)。當青年代滿時,將觸發清理所有三代,執行標記清除動作。之後,當中年代滿時,將會觸發清理中年代,老年代;最後,老年代觸發後只會清理自己

老年代的存活時間是最長的