ホームページ  >  記事  >  バックエンド開発  >  Pythonのガベージコレクションの仕組みを詳しく解説

Pythonのガベージコレクションの仕組みを詳しく解説

高洛峰
高洛峰オリジナル
2017-03-24 17:34:451376ブラウズ

1. ガベージ コレクションのメカニズム
Python のガベージ コレクションは主に参照カウントに基づいており、世代別コレクションによって補完されます。参照カウントの欠点は、循環参照の問題です。
Python では、オブジェクトへの参照の数が 0 の場合、Python 仮想マシンはこのオブジェクトのメモリを再利用します。

#encoding=utf-8
__author__ = 'kevinlu1010@qq.com'
 
class ClassA():
  def __init__(self):
    print 'object born,id:%s'%str(hex(id(self)))
  def __del__(self):
    print 'object del,id:%s'%str(hex(id(self)))
 
def f1():
  while True:
    c1=ClassA()
    del c1


f1() を実行するとそのような結果がループで出力され、プロセスが占有するメモリは基本的に変化しません

object born,id:0x237cf58
object del,id:0x237cf58


c1=ClassA() はオブジェクトを作成し、それを 0x237cf58 メモリに配置します。この時点で、変数はこのメモリを指しますが、このメモリの参照カウントは 1 です。 del c1 の後、c1 変数は 0x237cf58 メモリを指さなくなり、このメモリの参照カウントは 1 減って 0 になります。したがって、オブジェクトは破棄され、メモリが解放されます。
参照カウント +1 が発生する状況
オブジェクトが作成される (例: a=23)
オブジェクトが参照される (例: b=a)
オブジェクトが関数にパラメーターとして渡される (例: func(a))
オブジェクトは要素として使用され、コンテナに格納されます(例: list1=[a,a])
結果として参照カウントが -1 になる状況になります
オブジェクトのエイリアスは明示的に破棄されます(例: del a)
オブジェクトのエイリアスが新しいオブジェクトに割り当てられます (例: a=24)
オブジェクト そのスコープを離れると、たとえば f 関数の実行が完了すると、func 関数のローカル変数が割り当てられます (グローバル変数は割り当てられません)
コンテナオブジェクトが配置されている場所が破棄されるか、コンテナからオブジェクトが削除されます
demo

def func(c,d):
  print 'in func function', sys.getrefcount(c) - 1
 
 
print 'init', sys.getrefcount(11) - 1
a = 11
print 'after a=11', sys.getrefcount(11) - 1
b = a
print 'after b=1', sys.getrefcount(11) - 1
func(11)
print 'after func(a)', sys.getrefcount(11) - 1
list1 = [a, 12, 14]
print 'after list1=[a,12,14]', sys.getrefcount(11) - 1
a=12
print 'after a=12', sys.getrefcount(11) - 1
del a
print 'after del a', sys.getrefcount(11) - 1
del b
print 'after del b', sys.getrefcount(11) - 1
# list1.pop(0)
# print 'after pop list1',sys.getrefcount(11)-1
del list1
print 'after del list1', sys.getrefcount(11) - 1

出力:

init 24
after a=11 25
after b=1 26
in func function 28
after func(a) 26
after list1=[a,12,14] 27
after a=12 26
after del a 26
after del b 25
after del list1 24

質問: 関数を呼び出すと参照カウントが 2 増えるのはなぜですか
オブジェクトの参照カウントを確認してください
sys.getrefcount(a) はオブジェクトの参照カウントをチェックできますが、関数を呼び出すときに a が渡されるため、通常のカウントより 1 大きくなります。これにより、循環参照が 1 つ増加します。メモリリークにつながります

def f2():
  while True:
    c1=ClassA()
    c2=ClassA()
    c1.t=c2
    c2.t=c1
    del c1
    del c2
f2() が実行されると、プロセスによって占有されるメモリは増加し続けます。
object born,id:0x237cf30
object born,id:0x237cf58


c1とc2を作成した後、0x237cf30(c1に対応するメモリ、メモリ1として記録)、0x237cf58(c2に対応するメモリ、メモリ2として記録)の参照カウントが両方とも1になったので、t後にc1を実行します。 =c2 と c2.t=c1 で、これら 2 つのメモリの参照数は 2 になります。

del c1 の後、メモリ 1 のオブジェクトの参照数は 1 になります。0 ではないため、メモリ 1 のオブジェクトは 1 になります。 not は破棄されないので、メモリ 2 のオブジェクトの参照番号は 2 のままです。del c2 以降も同様に、メモリ 1 のオブジェクトとメモリ 2 のオブジェクトの参照番号は 1 になります。両方のオブジェクトは破棄できますが、循環参照のため、ガベージ コレクターはそれらをリサイクルせず、メモリ リークが発生します。


3. ガベージコレクション

deff3():
  # print gc.collect()
  c1=ClassA()
  c2=ClassA()
  c1.t=c2
  c2.t=c1
  del c1
  del c2
  print gc.garbage
  print gc.collect() #显式执行垃圾回收
  print gc.garbage
  time.sleep(10)
if __name__ == '__main__':
  gc.set_debug(gc.DEBUG_LEAK) #设置gc模块的日志
  f3()

出力: Python
gc: uncollectable <ClassA instance at 0230E918>
gc: uncollectable <ClassA instance at 0230E940>
gc: uncollectable <dict 0230B810>
gc: uncollectable <dict 02301ED0>
object born,id:0x230e918
object born,id:0x230e940


4
ガベージコレクション後のオブジェクトは gc.garbage リストに配置されます

gc.collect() は到達不能なオブジェクトの数 4 を返します等しい 2 つのオブジェクトとそれに対応する dict

ガベージ コレクションをトリガーする状況は 3 つあります:
1. gc.collect() の呼び出し、
2. gc モジュールのカウンターがしきい値に達したとき。
3. プログラムの終了時

IV. gc モジュールの共通機能の分析

Garbage Collector interface

gc モジュールは、開発者がガベージ コレクション オプションを設定するためのインターフェイスを提供します。前述したように、メモリ管理に参照カウント方式を使用する場合の欠点の 1 つは循環参照であり、gc モジュールの主な機能の 1 つは循環参照の問題を解決することです。 一般的に使用される関数: gc.set_debug(flags)

gc のデバッグ ログを設定します。通常は gc.DEBUG_LEAK に設定されます
gc.collect([世代])
明示的なガベージ コレクション、パラメーターを入力できます。0 は、 first one 第 1 世代のオブジェクト。1 は第 1 世代と第 2 世代のオブジェクトをチェックすることを意味します。2 は第 1 世代、第 2 世代、および第 3 世代のオブジェクトをチェックすることを意味します。パラメータが渡されない場合、完全なコレクションが実行されます。これは同等です。通過2へ。
到達不能なオブジェクトの数を返します
gc.set_threshold(threshold0[, Threshold1[, Threshold2])
自動ガベージコレクションの頻度を設定します。
gc.get_count()
現在の自動ガベージ コレクション カウンタを取得し、長さ 3 のリストを返します。
gc モジュールの自動ガベージ コレクション メカニズム
自動ガベージ コレクションを開始するには、gc モジュールをインポートし、is_enable()=True にする必要があります。
このメカニズムの主な機能は、到達不能なガベージ オブジェクトを検出して処理することです。
ガベージコレクション = ガベージ検査 + ガベージコレクション
Pythonでは、世代別コレクション方式が使用されます。オブジェクトを 3 つの世代に分割します。最初に、オブジェクトが作成されると、変更されたオブジェクトが第 1 世代のガベージ チェックを通過した場合は、第 2 世代に配置されます。世代のガベージ チェック。オブジェクトがガベージ チェックを通過した場合、そのオブジェクトは第 3 世代に配置されます。
gc モジュールには長さ 3 のカウンターのリストがあり、gc.get_count() を通じて取得できます。
例: (488,3,0)。ここで、488 は、Python によって割り当てられたメモリの数から、最後の世代のガベージ チェック以降に解放されたメモリの数を引いたものを指します。これはメモリの割り当てであり、参照カウントの増加ではないことに注意してください。例:

print gc.get_count() # (590, 8, 0)
a = ClassA()
print gc.get_count() # (591, 8, 0)
del a
print gc.get_count() # (590, 8, 0)


3是指距离上一次二代垃圾检查,一代垃圾检查的次数,同理,0是指距离上一次三代垃圾检查,二代垃圾检查的次数。
gc模快有一个自动垃圾回收的阀值,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10)
每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器
例如,假设阀值是(700,10,10):
当计数器从(699,3,0)增加到(700,3,0),gc模块就会执行gc.collect(0),即检查一代对象的垃圾,并重置计数器为(0,4,0)
当计数器从(699,9,0)增加到(700,9,0),gc模块就会执行gc.collect(1),即检查一、二代对象的垃圾,并重置计数器为(0,0,1)
当计数器从(699,9,9)增加到(700,9,9),gc模块就会执行gc.collect(2),即检查一、二、三代对象的垃圾,并重置计数器为(0,0,0)
其他
如果循环引用中,两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法,所以为了安全起见,gc模块会把对象放到gc.garbage中,但是不会销毁对象。
五.应用
 项目中避免循环引用
 引入gc模块,启动gc模块的自动清理循环引用的对象机制
 由于分代收集,所以把需要长期使用的变量集中管理,并尽快移到二代以后,减少GC检查时的消耗
 gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法,如果一定要使用该方法,同时导致了循环引用,需要代码显式调用gc.garbage里面的对象的__del__来打破僵局

以上がPythonのガベージコレクションの仕組みを詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。