Rumah >pembangunan bahagian belakang >Tutorial Python >Bagaimana untuk menguasai mekanisme pengumpulan sampah Python.
Terima kasih kepada mekanisme pengumpulan sampah automatik Python
, tidak perlu melepaskan objek secara manual apabila menciptanya dalam Python
. Ini sangat mesra pembangun dan membebaskan pembangun daripada perlu risau tentang pengurusan memori peringkat rendah. Tetapi jika anda tidak memahami mekanisme pengumpulan sampahnya, kod Python
yang anda tulis selalunya akan menjadi sangat tidak cekap.
Terdapat banyak algoritma kutipan sampah, terutamanya termasuk: 引用计数
, 标记-清除
, 分代收集
, dsb.
Dalam python
, algoritma kutipan sampah adalah berdasarkan 引用计数
, ditambah dengan dua mekanisme: 标记-清除
dan 分代收集
.
Prinsip pengiraan rujukan agak mudah:
Setiap objek mempunyai atribut pengiraan rujukan integer . Digunakan untuk merekodkan bilangan kali sesuatu objek dirujuk. Contohnya, objek A
, jika objek merujuk A
, maka kiraan rujukan A
ialah +1
. Apabila rujukan dipadamkan, kiraan rujukan A
ialah -1
. Apabila kiraan rujukan A
ialah 0, ini bermakna objek A
tidak lagi boleh digunakan dan dikitar semula secara langsung.
Dalam Python
, anda boleh mendapatkan nilai pembilang rujukan objek yang ditentukan melalui fungsi sys
modul getrefcount
Mari kita ambil contoh praktikal.
import sys class A(): def __init__(self): pass a = A() print(sys.getrefcount(a))
Jalankan kod di atas dan anda boleh mendapatkan hasil output sebagai 2
.
Seperti yang kita lihat di atas, selepas mencipta objek A
dan memberikan objek kepada pembolehubah a
, nilai pembilang rujukan objek ialah 2
. Jadi bila kaunter akan menjadi +1
dan bila kaunter akan menjadi -1
?
A() a=A() func(a) arr=[a,a]
Objek dimusnahkan secara jelas, seperti del a
. Pembolehubah ditugaskan semula kepada objek baharu, seperti a=0
. Objek meninggalkan skopnya, seperti func
apabila fungsi menyelesaikan pelaksanaan, func
pembolehubah tempatan dalam fungsi (pembolehubah global tidak akan).
Bekas di mana objek terletak dimusnahkan, atau objek dipadamkan daripada bekas.
Untuk lebih memahami kenaikan dan penurunan kaunter, kami menjalankan kod sebenar dan melihatnya dengan jelas sekali imbas.
import sys class A(): def __init__(self): pass print("创建对象 0 + 1 =", sys.getrefcount(A())) a = A() print("创建对象并赋值 0 + 2 =", sys.getrefcount(a)) b = a c = a print("赋给2个变量 2 + 2 =", sys.getrefcount(a)) b = None print("变量重新赋值 4 - 1 =", sys.getrefcount(a)) del c print("del对象 3 - 1 =", sys.getrefcount(a)) d = [a, a, a] print("3次加入列表 2 + 3 =", sys.getrefcount(a)) def func(c): print('传入函数 1 + 2 = ', sys.getrefcount(c)) func(A())
Keputusan output adalah seperti berikut:
创建对象 0 + 1 = 1 创建对象并赋值 0 + 2 = 2 赋给2个变量 2 + 2 = 4 变量重新赋值 4 - 1 = 3 del对象 3 - 1 = 2 3次加入列表 2 + 3 = 5 传入函数 1 + 2 = 3
Masa nyata. Apabila pembilang objek mencapai sifar, ini bermakna objek itu tidak boleh digunakan lagi, dan tidak perlu menunggu masa tertentu untuk melepaskan memori secara langsung.
Perlu memperuntukkan ruang mengira rujukan untuk objek, yang meningkatkan penggunaan memori.
Apabila objek yang perlu dilepaskan agak besar, seperti objek kamus, adalah perlu untuk menggelung dan menyarangkan panggilan ke semua objek yang dirujuk, yang mungkin mengambil masa yang lama.
Rujukan bulatan. Ini adalah kecacatan maut pengiraan rujukan tidak mempunyai penyelesaian, jadi algoritma pengumpulan sampah lain mesti digunakan untuk menambahnya.
Seperti yang dinyatakan dalam bahagian sebelumnya, algoritma pengiraan rujukan tidak dapat menyelesaikan masalah rujukan bulatan akan menyebabkan pembilang semua orang menjadi selamanya Ia tidak akan sama dengan 0
, menyebabkan masalah tidak dapat dikitar semula. Algoritma
标记-清除
digunakan terutamanya untuk masalah rujukan bulatan yang berpotensi Algoritma dibahagikan kepada 2 langkah: peringkat penandaan
class A(): def __init__(self): self.obj = None def func(): a = A() b = A() c = A() d = A() a.obj = b b.obj = a return [c, d] e = func()Dalam kod di atas, a dan b merujuk antara satu sama lain, dan e merujuk kepada c dan d. Keseluruhan hubungan rujukan ditunjukkan dalam rajah di bawah Jika algoritma kaunter rujukan digunakan, kedua-dua objek a dan b tidak akan dikitar semula. Menggunakan kaedah tanda dan jelas, bermula dari nod akar (iaitu objek e), tiga objek c, d dan e akan ditanda sebagai
, tetapi a dan b tidak boleh ditanda. Oleh itu a dan b akan dikitar semula. 可达
这是读者可能会有疑问,为什么确定根节点是e,而不会是a、b、c、d呢?这里就有讲究了,什么样的对象会被看成是根节点呢?一般而言,根节点的选取包括(但不限于)如下几种:
当前栈帧中的本地变量表中引用的对象,如各个线程被调用的方法堆栈中使用到的参数、 局部变量、 临时变量等。
全局静态变量
...
在执行垃圾回收过程中,程序会被暂停,即 stop-the-world
。这里很好理解:你妈妈在打扫房间的时候,肯定不允许你在房间内到处丢垃圾,要不然永远也无法打扫干净。
为了减少程序的暂停时间,采用 分代回收
( Generational Collection
)降低垃圾收集耗时。
分代回收基于这样的法则:
接大部分的对象生命周期短,大部分对象都是朝生夕灭。
经历越多次数的垃圾收集且活下来的对象,说明该对象越不可能是垃圾,应该越少去收集。
Python
中,对象一共有3种世代: G0
, G1
, G2
。
对象刚创建时为 G0
。
如果在一轮 GC
扫描中存活下来,则移至 G1
,处于 G1
的对象被扫描次数会减少。
如果再次在扫描中活下来,则进入 G2
,处于 G1
的对象被扫描次数将会更少。
当某世代中分配的对象数量与被释放的对象之差达到某个阈值的时,将触发对该代的扫描。当某世代触发扫描时,比该世代年轻的世代也会触发扫描。
那么这个阈值是多少呢?我们可以通过代码查看或者修改,示例代码如下
import gc threshold = gc.get_threshold() print("各世代的阈值:", threshold) # 设置各世代阈值 # gc.set_threshold(threshold0[, threshold1[, threshold2]]) gc.set_threshold(800, 20, 20)
输出结果如下:
各世代的阈值: (700, 10, 10)
Atas ialah kandungan terperinci Bagaimana untuk menguasai mekanisme pengumpulan sampah Python.. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!