Secara umum, Anda tidak perlu khawatir tentang pengumpul sampah dan bekerja dengan memori saat Anda menulis kode Python. Segera setelah objek tidak lagi diperlukan, Python secara otomatis membebaskan memori dari bawahnya. Meskipun demikian, memahami cara kerja GC akan membantu Anda menulis kode yang lebih baik.
Manajer memori
Tidak seperti bahasa populer lainnya, Python tidak membebaskan semua memori kembali ke sistem operasi segera setelah menghapus suatu objek. Sebagai gantinya, ia menggunakan manajer memori tambahan yang dirancang untuk objek kecil (yang ukurannya kurang dari 512 byte). Untuk bekerja dengan objek-objek seperti itu, ia mengalokasikan blok memori yang besar, di mana banyak objek kecil akan disimpan di masa depan.
Segera setelah salah satu objek kecil dihapus - memori dari bawahnya tidak masuk ke sistem operasi, Python meninggalkannya untuk objek baru dengan ukuran yang sama. Jika tidak ada objek yang tersisa di salah satu blok memori yang dialokasikan, maka Python dapat melepaskannya ke sistem operasi. Biasanya, blok dilepaskan ketika skrip menciptakan banyak objek sementara.
Jadi, jika proses Python berumur panjang mulai mengkonsumsi lebih banyak memori dari waktu ke waktu, ini tidak berarti bahwa kode Anda memiliki masalah kebocoran memori. Jika Anda ingin tahu lebih banyak tentang manajer memori dengan Python, Anda dapat membacanya di
artikel saya yang lain .
Algoritma Pengumpulan Sampah
Interpreter python standar (CPython) menggunakan dua algoritma sekaligus, penghitungan referensi dan pengumpul sampah generasi (selanjutnya disebut GC), lebih dikenal sebagai
modul gc standar dari Python.
Algoritma penghitungan tautan sangat sederhana dan efisien, tetapi memiliki satu kelemahan besar. Dia tidak tahu bagaimana mendefinisikan referensi melingkar. Karena inilah dalam python ada kolektor tambahan yang disebut GC generasi yang memantau objek dengan referensi melingkar yang potensial.
Dalam Python, algoritma penghitungan referensi bersifat mendasar dan tidak dapat dinonaktifkan, sedangkan GC adalah opsional dan dapat dinonaktifkan.
Tautan Algoritma Penghitungan
Algoritma penghitungan tautan adalah salah satu teknik pengumpulan sampah yang paling mudah. Objek dihapus segera setelah mereka tidak lagi direferensikan.
Dalam Python, variabel tidak menyimpan nilai, tetapi bertindak sebagai referensi ke objek. Yaitu, saat Anda menetapkan nilai ke variabel baru, pertama-tama sebuah objek dengan nilai ini dibuat, dan baru kemudian variabel tersebut mulai merujuk padanya. Beberapa variabel dapat merujuk satu objek.
Setiap objek di Python berisi bidang tambahan (referensi counter), yang menyimpan jumlah tautan ke sana. Segera setelah seseorang merujuk ke objek, bidang ini bertambah satu. Jika karena alasan tertentu tautannya hilang, maka bidang ini dikurangi satu.
Contoh ketika jumlah tautan meningkat:
- operator penugasan
- melewati argumen
- masukkan objek baru ke dalam sheet (jumlah tautan untuk objek bertambah)
- konstruksi form foo = bar (foo mulai merujuk ke objek yang sama dengan bar)
Segera setelah penghitung referensi untuk objek tertentu mencapai nol, penafsir memulai proses penghancuran objek. Jika objek jauh berisi tautan ke objek lain, maka tautan ini juga dihapus. Dengan demikian, penghapusan satu objek mungkin memerlukan penghapusan yang lain.
Misalnya, jika daftar dihapus, maka jumlah referensi di semua elemennya dikurangi satu. Jika semua objek dalam daftar tidak digunakan di tempat lain, maka mereka juga akan dihapus.
Variabel yang dideklarasikan di luar fungsi, kelas, dan blok disebut global. Biasanya, siklus hidup variabel tersebut sama dengan umur proses Python. Dengan demikian, jumlah referensi ke objek yang dirujuk oleh variabel global tidak pernah turun ke nol.
Variabel yang dideklarasikan di dalam blok (fungsi, kelas) memiliki visibilitas lokal (mis. Mereka hanya terlihat di dalam blok). Segera setelah interpreter python keluar dari blok, ia menghancurkan semua tautan yang dibuat oleh variabel lokal di dalamnya.
Anda selalu dapat memeriksa jumlah tautan menggunakan fungsi
sys.getrefcount
.
Contoh penghitung tautan:
foo = []
Alasan utama bahwa juru bahasa standar (CPython) menggunakan penghitung referensi adalah historis. Saat ini ada banyak perdebatan tentang pendekatan ini. Beberapa orang percaya bahwa seorang pemulung dapat jauh lebih efisien tanpa algoritma penghitungan tautan. Algoritma ini memiliki banyak masalah, seperti tautan sirkuler, blokir ulir, serta overhead tambahan untuk memori dan cpu.
Keuntungan utama dari algoritma ini adalah bahwa objek dihapus segera setelah mereka tidak diperlukan.
Pengumpul sampah opsional
Mengapa kita perlu algoritma tambahan ketika kita sudah memiliki penghitungan referensi?
Sayangnya, algoritma penghitungan tautan klasik memiliki satu kelemahan besar - ia tidak tahu cara menemukan tautan melingkar. Loopback terjadi ketika satu atau lebih objek saling referensi.
Dua contoh:

Seperti yang Anda lihat, objek pertama mengacu pada dirinya sendiri, sedangkan
object1
dan
object2
merujuk. Untuk objek seperti itu, jumlah referensi akan selalu 1.
Demo python:
import gc
Pada contoh di atas, instruksi del menghapus referensi ke objek kita (bukan objek itu sendiri). Setelah Python mengeksekusi pernyataan del, objek-objek ini menjadi tidak dapat diakses dari kode Python. Namun, dengan modul gc dimatikan, mereka masih akan tetap dalam memori, seperti mereka memiliki referensi melingkar dan penghitung mereka masih satu. Anda dapat secara visual menjelajahi hubungan tersebut menggunakan perpustakaan
objgraph
.
Untuk memperbaiki masalah ini, algoritma tambahan yang dikenal sebagai
modul gc ditambahkan dengan Python 1.5. Satu-satunya tugas yang merupakan penghapusan objek siklik yang tidak ada lagi akses dari kode.
Loopback hanya dapat terjadi pada objek "wadah". Yaitu di objek yang dapat menyimpan objek lain, seperti daftar, kamus, kelas, dan tupel. GC tidak melacak tipe sederhana dan tidak berubah, kecuali untuk tupel. Beberapa tupel dan kamus juga dikecualikan dari daftar pelacakan ketika kondisi tertentu terpenuhi. Dengan semua objek lain, algoritma penghitungan referensi dijamin untuk mengatasinya.
Ketika GC dipicu
Berbeda dengan algoritma penghitungan referensi, siklik GC tidak bekerja secara real time dan berjalan secara berkala. Setiap proses kolektor menciptakan jeda mikro dalam kode, sehingga CPython (juru bahasa standar) menggunakan berbagai heuristik untuk menentukan frekuensi pengumpul sampah.
Pengumpul sampah siklik membagi semua benda menjadi 3 generasi (generasi). Benda-benda baru jatuh ke generasi pertama. Jika fasilitas baru selamat dari proses pengumpulan sampah, maka pindah ke generasi berikutnya. Semakin tinggi generasi, semakin jarang dipindai untuk mencari sampah. Karena objek baru sering memiliki masa hidup yang sangat singkat (bersifat sementara), masuk akal untuk mewawancarai mereka lebih sering daripada yang telah melewati beberapa tahap pengumpulan sampah.
Setiap generasi memiliki penghitung khusus dan ambang respons, setelah mencapai yang memicu proses pengumpulan sampah. Setiap penghitung menyimpan jumlah alokasi dikurangi jumlah alokasi dalam satu generasi. Segera setelah objek kontainer dibuat dengan Python, ia memeriksa penghitung ini. Jika kondisinya berhasil, maka proses pengumpulan sampah dimulai.
Jika beberapa generasi atau lebih telah melewati ambang pintu sekaligus, maka generasi paling senior yang dipilih. Ini disebabkan oleh fakta bahwa generasi yang lebih tua juga memindai semua yang sebelumnya. Untuk mengurangi jumlah pengumpulan sampah yang dijeda untuk objek yang berumur panjang, generasi tertua memiliki
serangkaian kondisi tambahan .
Ambang standar untuk generasi masing-masing diatur ke
700, 10 10
, tetapi Anda selalu dapat mengubahnya menggunakan fungsi
gc.get_threshold gc.set_threshold
.
Algoritma Pencarian Lingkaran
Penjelasan lengkap tentang algoritma pencarian putaran akan membutuhkan artikel terpisah. Singkatnya, GC mengulangi setiap objek dari generasi yang dipilih dan untuk sementara menghapus semua tautan dari satu objek (semua tautan yang dirujuk oleh objek ini). Setelah lulus penuh, semua objek dengan jumlah tautan kurang dari dua dianggap tidak dapat diakses dari python dan dapat dihapus.
Untuk pemahaman yang lebih dalam, saya sarankan membaca (catatan penerjemah: bahan bahasa Inggris)
deskripsi asli dari algoritma dari Neil Schemenauer dan fungsi
collect
dari
sumber CPython . Deskripsi dari
Quora dan
postingan tentang pemulung juga bisa bermanfaat.
Perlu dicatat bahwa masalah dengan destruktor yang dijelaskan dalam deskripsi asli algoritma telah diperbaiki sejak Python 3.4 (lebih detail dalam
PEP 442 ).
Kiat Pengoptimalan
Loop sering terjadi dalam tugas-tugas kehidupan nyata, mereka dapat ditemukan dalam masalah dengan grafik, daftar tertaut, atau dalam struktur data di mana Anda perlu melacak hubungan antara objek. Jika program Anda memiliki beban yang tinggi dan menuntut penundaan, maka, jika mungkin, loop sebaiknya dihindari.
Di tempat-tempat di mana Anda secara sadar menggunakan tautan sirkuler, Anda dapat menggunakan tautan "lemah". Lemah tautan diimplementasikan dalam modul
lemahref dan, tidak seperti tautan biasa, tidak mempengaruhi penghitung tautan dengan cara apa pun. Jika objek dengan referensi lemah ternyata dihapus, maka tidak
None
dikembalikan.
Dalam beberapa kasus, berguna untuk menonaktifkan pembangunan otomatis oleh modul gc dan memanggilnya secara manual. Untuk melakukan ini, panggil saja
gc.disable()
lalu panggil
gc.collect()
secara manual.
Cara menemukan dan men-debug tautan melingkar
Debugging loop bisa menyakitkan, terutama jika kode Anda menggunakan banyak modul pihak ketiga.
Modul gc menyediakan fungsi pembantu yang dapat membantu debugging. Jika parameter GC diatur ke bendera
DEBUG_SAVEALL
, maka semua objek yang tidak dapat diakses akan ditambahkan ke daftar
gc.garbage
.
import gc gc.set_debug(gc.DEBUG_SAVEALL) print(gc.get_count()) lst = [] lst.append(lst) list_id = id(lst) del lst gc.collect() for item in gc.garbage: print(item) assert list_id == id(item)
Setelah Anda mengidentifikasi tempat masalah - itu dapat divisualisasikan menggunakan
objgraph .

Kesimpulan
Proses pengumpulan sampah utama dilakukan oleh algoritma penghitungan tautan, yang sangat sederhana dan tidak memiliki pengaturan. Algoritme tambahan hanya digunakan untuk mencari dan menghapus objek dengan referensi melingkar.
Anda tidak boleh terlibat dalam optimalisasi dini kode untuk pengumpul sampah, dalam praktiknya, masalah dengan pengumpulan sampah sangat jarang.
PS: Saya penulis artikel ini, kamu bisa bertanya.