Menulis data = [1, 2, 3] terasa gratis, tetapi di balik layar interpreter Python mengalokasikan memori, mencatat metadata, dan menyiapkan mekanisme pembersihan. Ketika program menjadi lambat atau menghabiskan RAM raksasa, pemahaman tentang cara Python mengelola memori adalah titik awal diagnosis yang tepat. Pada Chapter ini kita akan melihat ke mana object pergi, berapa besar biaya tiap object, dan apa yang sebenarnya terjadi ketika object dihapus.
Object Hidup di Heap, Nama Hanyalah Label #
Python menyimpan setiap value — angka, string, list, bahkan function — sebagai object lengkap di area memori yang disebut heap. Sebuah nama seperti data bukan wadah yang menampung isi, melainkan hanya label yang menunjuk ke object di heap. Perilaku ini sudah kita bahas tuntas pada Chapter Memahami Object dan Reference; di sini kita pakai fakta tersebut sebagai fondasi:
a = ["Budi", "Siti"]
b = a
b.append("Ani")
print("isi a :", a)
print("a is b:", a is b)
print("a == b:", a == b)
c = list(a)
c.append("Joko")
print("isi a :", a)
print("a is c:", a is c)Output yang Dihasilkan:
isi a : ['Budi', 'Siti', 'Ani']
a is b: True
a == b: True
isi a : ['Budi', 'Siti', 'Ani']
a is c: FalseMari kita telaah baris-baris kode di atas:
- Baris 1 (
a = ["Budi", "Siti"]): interpreter membuat object list di heap, lalu mengikat namaake object tersebut. - Baris 2 (
b = a): tidak ada penyalinan data; namabkini menunjuk ke object list yang sama. Karena itub.append("Ani")juga terlihat daria. - Baris 3 (
a is b): operatorismengembalikanTruekarena kedua nama menunjuk ke satu object yang sama. - Baris terakhir (
c = list(a)): constructorlist()membuat object baru — perubahan padactidak menyentuha, dana is cbernilaiFalse.
Konsekuensi praktisnya penting: menghapus satu nama tidak serta-merta menghapus object. Object baru benar-benar hilang ketika sudah tidak ada nama, elemen container, atau reference lain yang menunjuk ke object tersebut — mekanisme penentu kapan hal itu terjadi kita bahas pada Chapter Reference Counting dan Garbage Collection.
Setiap Object Membawa Overhead #
Object Python tidak hanya menyimpan data mentah. Setiap object membawa header berisi reference count dan pointer ke type object. Fungsi sys.getsizeof() melaporkan ukuran object dalam byte:
import sys
print("int 0 :", sys.getsizeof(0))
print("int 10**20 :", sys.getsizeof(10**20))
print("str kosong :", sys.getsizeof(""))
print("str 'a' :", sys.getsizeof("a"))
print("list kosong :", sys.getsizeof([]))
print("list [1, 2, 3] :", sys.getsizeof([1, 2, 3]))
print("dict kosong :", sys.getsizeof({}))
print("set kosong :", sys.getsizeof(set()))
print("None :", sys.getsizeof(None))Output yang Dihasilkan:
int 0 : 28
int 10**20 : 36
str kosong : 41
str 'a' : 42
list kosong : 56
list [1, 2, 3] : 88
dict kosong : 64
set kosong : 216
None : 16Angka 28 untuk integer 0 menunjukkan bahwa integer kecil sekalipun menempati 28 byte di Python 3.12 — hampir seluruhnya header dan struktur internal, bukan angkanya. Integer 10**20 butuh 36 byte karena digit-digitnya lebih panjang. String kosong sudah memakan 41 byte sebelum satu karakter pun disimpan.
Angka 88 untuk list berisi 3 elemen juga memperlihatkan strategi over-allocation: interpreter sengaja menyediakan slot lebih dari isi saat ini, sehingga append() berikutnya tidak harus langsung memindahkan seluruh isi ke blok memori baru.
Pymalloc: Allocator untuk Object Kecil #
Mengalokasikan memori ke sistem operasi untuk setiap integer 28 byte tentu sangat mahal. Karena itu CPython memakai allocator khusus bernama pymalloc: memori dikelola dalam arena besar yang dipecah menjadi pool, lalu menjadi block untuk object berukuran kecil — di Python 3.12, object sampai sekitar 512 byte dilayani jalur ini. Object kecil yang dihapus meninggalkan block kosong yang langsung dipakai ulang untuk object kecil berikutnya, tanpa menyentuh sistem operasi. Object besar (misalnya string berukuran beberapa megabyte) melewati pymalloc dan dialokasikan langsung oleh allocator standar.
Angka pasti seperti ukuran arena adalah detail internal CPython yang dapat berubah antar versi. Yang perlu kita pegang adalah idenya: Python membeli memori dalam jumlah besar sekali jalan, lalu mengelola pembagian kecilnya sendiri.
Memori yang Dibebaskan Tidak Langsung Kembali ke OS #
Bagian ini sering mengejutkan: ketika object dihapus, memori kembali ke kolam internal Python, bukan ke sistem operasi. Kita bisa membuktikan bahwa Python benar-benar membebaskan object dengan module tracemalloc:
import tracemalloc
tracemalloc.start()
daftar = [i * 2 for i in range(1_000_000)]
current, peak = tracemalloc.get_traced_memory()
print(f"setelah alokasi : current = {current:,} byte, peak = {peak:,} byte")
del daftar
current, peak = tracemalloc.get_traced_memory()
print(f"setelah del : current = {current:,} byte, peak = {peak:,} byte")Output yang Dihasilkan:
setelah alokasi : current = 40,444,944 byte, peak = 40,444,944 byte
setelah del : current = 464 byte, peak = 40,445,288 byteAngka current anjlok dari sekitar 40 MB ke ratusan byte — object-nya sungguh dibebaskan (8 MB untuk array pointer plus sekitar 28 byte per object integer). Namun bila kita mengamati proses Python lewat monitor sistem operasi seperti top, ukuran Resident Set Size (RSS) — memori yang benar-benar dipegang proses — umumnya tidak ikut turun. Arena pymalloc tetap dipertahankan untuk dipakai ulang; sebagian barunya baru dikembalikan ke OS setelah benar-benar kosong dalam jangka panjang.
Akibat praktisnya: melihat RAM proses Python yang tidak pernah turun belum tentu berarti kebocoran. Kebocoran sejati terjadi ketika object yang tidak dibutuhkan tetap direferensikan — misalnya daftar yang terus memanjang di global variable.
Sekilas Modul gc #
Dua lapis pembersihan bekerja di Python: reference counting yang berjalan instan di setiap operasi, dan garbage collector siklis pada module gc yang menyapu object yang saling merujuk. Module gc aktif secara bawaan, dan interaksinya — termasuk cara membuktikan bahwa reference counting saja tidak cukup — menjadi fokus Chapter berikutnya, Reference Counting dan Garbage Collection.