Python のGC(ガベージコレクション)と参照カウンタ

pythonを利用する上で、便利な表記などの個人的なメモである。基本的な部分は触れておらず、対象も自分が便利だなと思ったものに限定している。

github

  • githubのjupyter notebook形式のファイルはこちら

google colaboratory

  • google colaboratory で実行する場合はこちら

筆者の環境

!sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.6
BuildVersion:	18G103
!python -V
Python 3.8.5

GCとgetrefcount

GCの条件について色々する機会があったのでメモとして残しおく。

pythonは基本的にはガベージコレクションが採用されており、CやC++のように明示的にメモリを解放しなくても不要になったオブジェクトに関しては自動的に解放されるようになっている。それを実現している参照カウンタである。すべてのオブジェクトには、参照カウンタが内蔵されており、ある別のオブジェクトからそのオブジェクトへ参照があったら、参照カウンタをインクリメントする。また、その参照が削除されたらデクリメントを行い、カウンタが0になったらそのオブジェクトを解放する。

普段あまり参照カウンタには注意を払ったことがなかったのですが、今回この数字を元に色々解析したので、その概要だけ記録しておく。

import sys

class A():
  def __init__(self, a, b):
    self.a = a
    self.b = b

オブジェクトが作成され、さらにsys.getrefcountから参照されるので、参照カウンタは2になる。

a = A('a', 1)
sys.getrefcount(a)
2

bからも参照されるので3になる。

b = a
sys.getrefcount(a)
3

bを削除すると2になる。

b = None
sys.getrefcount(a)
2

インスタンス変数へ参照しても、インクリメントされない。

c = a.b
sys.getrefcount(a)
2

GC

通常、GCはdelを行った後、gc.collet()を明示的に行う。 ただ、del後もメモリからその値が削除されるわけではなく、あくまでもpythonから参照できなくなるだけである。

import gc

b = A('b', 2)
del b

print(gc.get_stats()[2])
gc.collect()
print(gc.get_stats()[2])
{'collections': 3, 'collected': 664, 'uncollectable': 0}
{'collections': 4, 'collected': 810, 'uncollectable': 0}

gc後は参照カウンタは取得できない。

sys.getrefcount(b)
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-31-e0d8d1240a7d> in <module>
----> 1 sys.getrefcount(b)


NameError: name 'b' is not defined

参考記事