Saya memiliki beberapa pertanyaan terkait penggunaan memori dalam contoh berikut.
Jika saya menjalankan interpreter,
foo = ['bar' for _ in xrange(10000000)]
memori nyata yang digunakan pada mesin saya naik ke
80.9mb
. Lalu saya,del foo
memori nyata turun, tetapi hanya untuk
30.4mb
. Penerjemah menggunakan4.4mb
baseline jadi apa untungnya tidak melepaskan26mb
memori ke OS? Apakah itu karena Python "merencanakan ke depan", berpikir bahwa Anda dapat menggunakan memori sebanyak itu lagi?Mengapa ia merilis
50.5mb
secara khusus - berapakah jumlah yang dilepaskan berdasarkan?Apakah ada cara untuk memaksa Python untuk melepaskan semua memori yang digunakan (jika Anda tahu Anda tidak akan menggunakan banyak memori lagi)?
CATATAN
Pertanyaan ini berbeda dengan Bagaimana saya dapat secara eksplisit membebaskan memori dengan Python?
karena pertanyaan ini terutama berkaitan dengan peningkatan penggunaan memori dari awal bahkan setelah penerjemah membebaskan benda melalui pengumpulan sampah (dengan menggunakan gc.collect
atau tidak).
sumber
gc.collect
.Jawaban:
Memori yang dialokasikan pada heap dapat dikenakan tanda air tinggi. Ini rumit oleh optimasi internal Python untuk mengalokasikan objek kecil (
PyObject_Malloc
) dalam 4 kolam KiB, dikelompokkan untuk ukuran alokasi pada kelipatan 8 byte - hingga 256 byte (512 byte dalam 3,3). Kolam itu sendiri berada di 256 arena KiB, jadi jika hanya satu blok dalam satu kolam digunakan, seluruh arena 256 KiB tidak akan dilepaskan. Dalam Python 3.3, pengalokasi objek kecil dialihkan untuk menggunakan peta memori anonim, bukan tumpukan, sehingga harus lebih baik dalam melepaskan memori.Selain itu, tipe bawaan mempertahankan daftar bebas dari objek yang sebelumnya dialokasikan yang mungkin atau mungkin tidak menggunakan pengalokasi objek kecil. The
int
tipe mempertahankan daftar bebas dengan memori yang dialokasikan sendiri, dan kliring membutuhkan meneleponPyInt_ClearFreeList()
. Ini bisa disebut secara tidak langsung dengan melakukan penuhgc.collect
.Cobalah seperti ini, dan beri tahu saya apa yang Anda dapatkan. Inilah tautan untuk psutil.Process.memory_info .
Keluaran:
Edit:
Saya beralih ke pengukuran relatif terhadap ukuran proses VM untuk menghilangkan efek dari proses lain dalam sistem.
C runtime (misalnya glibc, msvcrt) menyusut tumpukan ketika ruang kosong yang berdekatan di bagian atas mencapai ambang batas yang konstan, dinamis, atau dapat dikonfigurasi. Dengan glibc Anda dapat menyetel ini dengan
mallopt
(M_TRIM_THRESHOLD). Mengingat hal ini, tidak mengherankan jika tumpukan menyusut lebih - bahkan lebih banyak - daripada blok yang Andafree
.Dalam 3.x
range
tidak membuat daftar, jadi tes di atas tidak akan membuat 10 jutaint
objek. Bahkan jika itu terjadi,int
tipe 3.x pada dasarnya adalah 2.xlong
, yang tidak menerapkan daftar freelist.sumber
memory_info()
sebagai gantiget_memory_info()
danx
didefinisikanint
bahkan dalam Python 3, tetapi masing-masing menggantikan yang terakhir dalam variabel loop sehingga tidak semua ada sekaligus.Saya menduga pertanyaan yang sangat Anda pedulikan di sini adalah:
Tidak, tidak ada. Tetapi ada solusi yang mudah: proses anak.
Jika Anda membutuhkan penyimpanan sementara 500MB selama 5 menit, tetapi setelah itu Anda perlu menjalankan selama 2 jam lagi dan tidak akan menyentuh memori sebanyak itu lagi, menelurkan proses anak untuk melakukan pekerjaan intensif-memori. Ketika proses anak hilang, memori dilepaskan.
Ini tidak sepenuhnya sepele dan gratis, tetapi cukup mudah dan murah, yang biasanya cukup baik untuk perdagangan yang berharga.
Pertama, cara termudah untuk membuat proses anak adalah dengan
concurrent.futures
(atau, untuk 3.1 dan sebelumnya,futures
backport pada PyPI):Jika Anda perlu sedikit kontrol lagi, gunakan
multiprocessing
modul.Biaya adalah:
mmap
ped atau sebaliknya; API memori bersama dimultiprocessing
; dll.)struct
-able atau idealnyactypes
-able).sumber
eryksun telah menjawab pertanyaan # 1, dan saya telah menjawab pertanyaan # 3 (yang asli # 4), tetapi sekarang mari kita jawab pertanyaan # 2:
Apa yang menjadi
malloc
dasarnya adalah, pada akhirnya, seluruh rangkaian kebetulan di dalam Python dan itu sangat sulit diprediksi.Pertama, tergantung pada bagaimana Anda mengukur memori, Anda mungkin hanya mengukur halaman yang sebenarnya dipetakan ke dalam memori. Dalam hal ini, setiap kali halaman ditukar oleh pager, memori akan muncul sebagai "dibebaskan", meskipun belum dibebaskan.
Atau Anda mungkin mengukur halaman yang sedang digunakan, yang mungkin atau mungkin tidak menghitung halaman yang dialokasikan tetapi tidak pernah disentuh (pada sistem yang optimis alokasi berlebihan, seperti linux), halaman yang dialokasikan tetapi ditandai
MADV_FREE
, dll.Jika Anda benar-benar mengukur halaman yang dialokasikan (yang sebenarnya bukan hal yang sangat berguna untuk dilakukan, tetapi tampaknya itu yang Anda tanyakan), dan halaman benar-benar telah dialokasikan, dua keadaan di mana hal ini dapat terjadi: Entah Anda telah digunakan
brk
atau setara untuk mengecilkan segmen data (sangat jarang saat ini), atau Anda telah menggunakanmunmap
atau mirip untuk melepaskan segmen yang dipetakan. (Secara teoritis juga ada varian kecil untuk yang terakhir, dalam hal itu ada cara untuk melepaskan bagian dari segmen yang dipetakan — misalnya, mencurinya denganMAP_FIXED
untukMADV_FREE
segmen yang Anda segera hapus peta.)Tetapi sebagian besar program tidak secara langsung mengalokasikan hal-hal dari halaman memori; mereka menggunakan
malloc
pengalokasi-gaya. Saat Anda meneleponfree
, pengalokasi hanya dapat merilis halaman ke OS jika Anda kebetulan menjadifree
objek langsung terakhir dalam pemetaan (atau di halaman N terakhir dari segmen data). Tidak mungkin aplikasi Anda dapat memprediksi hal ini secara wajar, atau bahkan mendeteksi bahwa itu terjadi sebelumnya.CPython membuat ini lebih rumit - ia memiliki pengalokasi objek 2-tingkat kustom di atas pengalokasi memori kustom di atas
malloc
. (Lihat komentar sumber untuk penjelasan yang lebih terperinci.) Dan di atas itu, bahkan pada level C API, apalagi Python, Anda bahkan tidak secara langsung mengontrol ketika objek level atas dideallocated.Jadi, ketika Anda merilis objek, bagaimana Anda tahu apakah itu akan melepaskan memori ke OS? Yah, pertama-tama Anda harus tahu bahwa Anda telah merilis referensi terakhir (termasuk referensi internal apa pun yang Anda tidak tahu), memungkinkan GC untuk membatalkan alokasi itu. (Tidak seperti implementasi lainnya, setidaknya CPython akan membatalkan alokasi objek segera setelah diizinkan.) Ini biasanya membatalkan alokasi setidaknya dua hal di tingkat berikutnya ke bawah (misalnya, untuk string, Anda melepaskan
PyString
objek, dan buffer string ).Jika Anda melakukan deallocate objek, untuk mengetahui apakah ini menyebabkan tingkat berikutnya turun untuk mendelallocasi blok penyimpanan objek, Anda harus mengetahui keadaan internal pengalokasi objek, serta bagaimana itu diterapkan. (Ini jelas tidak dapat terjadi kecuali Anda membatalkan alokasi hal terakhir di blok, dan bahkan kemudian, itu mungkin tidak terjadi.)
Jika Anda melakukan deallocate blok penyimpanan objek, untuk mengetahui apakah ini menyebabkan
free
panggilan, Anda harus mengetahui keadaan internal pengalokasi PyMem, serta bagaimana itu diterapkan. (Sekali lagi, Anda harus deallocating blok yang terakhir digunakan dalammalloc
wilayah ed, dan bahkan kemudian, itu mungkin tidak terjadi.)Jika Anda melakukan
free
suatumalloc
wilayah ed, untuk mengetahui apakah ini menyebabkanmunmap
atau setara (ataubrk
), Anda harus tahu keadaan internalmalloc
, serta bagaimana hal itu dilaksanakan. Dan yang ini, tidak seperti yang lain, sangat spesifik platform. (Dan sekali lagi, Anda umumnya harus membatalkan alokasi yang terakhir digunakanmalloc
dalam suatummap
segmen, dan bahkan kemudian, itu mungkin tidak terjadi.)Jadi, jika Anda ingin memahami mengapa itu terjadi untuk melepaskan tepat 50,5mb, Anda harus melacaknya dari bawah ke atas. Mengapa tidak
malloc
memetakan nilai halaman 50,5mb saat Anda melakukan satufree
panggilan atau lebih (untuk mungkin sedikit lebih dari 50,5mb)? Anda harus membaca platform Andamalloc
, dan kemudian berjalan di berbagai tabel dan daftar untuk melihat kondisi saat ini. (Pada beberapa platform, bahkan mungkin menggunakan informasi tingkat sistem, yang hampir tidak mungkin ditangkap tanpa membuat snapshot dari sistem untuk memeriksa offline, tetapi untungnya ini biasanya bukan masalah.) Dan kemudian Anda harus lakukan hal yang sama pada 3 level di atas itu.Jadi, satu-satunya jawaban yang berguna untuk pertanyaan itu adalah "Karena."
Kecuali jika Anda melakukan pengembangan terbatas sumber daya (misalnya tertanam), Anda tidak punya alasan untuk peduli tentang detail ini.
Dan jika Anda sedang melakukan pengembangan sumber daya terbatas, mengetahui rincian ini tidak berguna; Anda cukup banyak harus melakukan end-run di sekitar semua level tersebut dan khususnya
mmap
memori yang Anda butuhkan di level aplikasi (mungkin dengan satu pengalokasi zona aplikasi khusus yang sederhana, dipahami dengan baik di antaranya).sumber
Pertama, Anda mungkin ingin menginstal lirikan:
Kemudian jalankan di terminal!
Dalam kode Python Anda, tambahkan di awal file, berikut ini:
Setelah menggunakan variabel "Besar" (misalnya: myBigVar), Anda ingin melepaskan memori, tuliskan kode python Anda sebagai berikut:
Di terminal lain, jalankan kode python Anda dan amati di terminal "lirikan", bagaimana memori dikelola di sistem Anda!
Semoga berhasil!
PS Saya menganggap Anda bekerja pada sistem Debian atau Ubuntu
sumber