Manajemen memori python

Halo semuanya! Jadi akhir pekan panjang Maret berakhir. Kami ingin mendedikasikan publikasi pasca-liburan pertama untuk orang yang dicintai oleh banyak kursus - "Pengembang Python" , yang dimulai dalam waktu kurang dari 2 minggu. Ayo pergi.

Isi

  1. Memori adalah buku kosong
  2. Manajemen Memori: Dari Perangkat Keras ke Perangkat Lunak
  3. Implementasi basis Python
  4. Konsep Global Interpreter Lock (GIL)
  5. Pengumpul sampah
  6. Manajemen Memori dalam CPython:
    • Kolam renang
    • Blok
    • Arena
  7. Kesimpulan



Pernahkah Anda bertanya-tanya bagaimana Python di belakang panggung memproses data Anda? Bagaimana variabel Anda disimpan dalam memori? Pada titik apa mereka dipindahkan?
Pada artikel ini, kita akan mempelajari lebih dalam struktur internal Python untuk memahami cara kerja manajemen memori.

Setelah membaca artikel ini, Anda:

  • Pelajari lebih lanjut tentang operasi tingkat rendah, terutama memori.
  • Memahami bagaimana Python mengabstraksi operasi tingkat rendah.
  • Pelajari tentang algoritma manajemen memori dengan Python.

Mengetahui struktur internal Python akan memberikan pemahaman yang lebih baik tentang prinsip-prinsip perilakunya. Saya harap Anda dapat melihat Python dari perspektif baru. Di balik layar, ada begitu banyak operasi logis untuk membuat program Anda berfungsi dengan baik.

Memori adalah buku kosong.

Anda dapat membayangkan memori komputer sebagai buku kosong, menunggunya menulis banyak cerita pendek. Belum ada apa-apa di halamannya, tetapi penulis akan segera muncul yang ingin menulis cerita mereka di dalamnya. Untuk melakukan ini, mereka akan membutuhkan tempat.
Karena mereka tidak dapat menulis satu cerita di atas yang lain, mereka harus sangat berhati-hati tentang halaman yang mereka tulis. Sebelum Anda mulai menulis, mereka berkonsultasi dengan manajer buku. Manajer memutuskan di mana dalam buku itu penulis dapat menuliskan cerita mereka.

Karena buku itu telah ada selama bertahun-tahun, banyak cerita di dalamnya menjadi ketinggalan zaman. Ketika tidak ada yang membaca atau membahas sejarah, mereka menghapusnya untuk memberi jalan bagi cerita baru.
Pada intinya, memori komputer seperti buku kosong. Blok memori berkelanjutan dengan panjang tetap biasanya disebut halaman, jadi analogi ini berguna.

Penulis dapat berbagai aplikasi atau proses yang perlu menyimpan data dalam memori. Seorang manajer yang memutuskan di mana penulis dapat menulis cerita mereka memainkan peran manajer memori - penyortir. Dan orang yang menghapus cerita lama adalah seorang pemulung.

Manajemen Memori: Dari Perangkat Keras ke Perangkat Lunak

Manajemen memori adalah proses di mana aplikasi perangkat lunak membaca dan menulis data. Manajer memori menentukan tempat untuk meletakkan data program. Karena jumlah memori tentu saja, seperti jumlah halaman dalam buku, maka, manajer perlu menemukan ruang kosong untuk menyediakannya untuk digunakan oleh aplikasi. Proses ini disebut "alokasi memori".

Di sisi lain, ketika data tidak lagi dibutuhkan, itu bisa dihapus. Dalam hal ini, mereka berbicara tentang membebaskan memori. Tapi dari mana itu dibebaskan dan dari mana asalnya?
Di suatu tempat di dalam komputer ada perangkat fisik yang menyimpan data ketika Anda menjalankan program Python. Kode python melewati banyak level abstraksi sebelum sampai ke perangkat ini.

Salah satu level utama yang terletak di atas peralatan (RAM, hard disk, dll.) Adalah sistem operasi. Ia mengelola permintaan baca dan tulis ke memori.
Di atas sistem operasi ada lapisan aplikasi di mana ada salah satu implementasi Python (kabel ke OS Anda atau diunduh dari python.org). Manajemen memori untuk kode dalam bahasa pemrograman ini diatur oleh alat Python khusus. Algoritma dan struktur yang digunakan Python untuk mengelola memori adalah topik utama artikel ini.

Implementasi basis Python

Implementasi dasar dari Python, atau "pure Python", adalah CPython ditulis dalam C.
Saya sangat terkejut ketika saya pertama kali mendengarnya. Bagaimana bisa satu bahasa ditulis dalam bahasa lain ?! Ya, tentu saja tidak secara harfiah, tetapi idenya kira-kira seperti ini.

Bahasa Python dijelaskan dalam manual referensi khusus dalam bahasa Inggris . Namun, panduan ini saja tidak terlalu berguna. Anda masih memerlukan alat untuk menafsirkan kode yang ditulis oleh aturan direktori.

Anda juga akan memerlukan sesuatu untuk mengeksekusi kode di komputer Anda. Implementasi Python dasar menyediakan kedua kondisi. Itu mengubah kode Python menjadi instruksi yang dieksekusi di mesin virtual.

Catatan: Mesin virtual mirip dengan komputer fisik, tetapi mereka tertanam dalam perangkat lunak. Mereka memproses instruksi dasar yang mirip dengan kode perakitan .


Python adalah bahasa pemrograman yang ditafsirkan. Kode Python Anda dikompilasi menggunakan instruksi yang lebih mudah dipahami oleh komputer - bytecode . Instruksi ini ditafsirkan oleh mesin virtual ketika Anda menjalankan kode.

Pernahkah Anda melihat file dengan ekstensi .pyc atau folder __pycache__ ? Ini adalah bytecode yang sama yang ditafsirkan oleh mesin virtual.
Penting untuk memahami bahwa ada implementasi lain selain CPython, misalnya IronPython , yang mengkompilasi dan berjalan di Microsoft Common Language Runtime (CLR). Jython mengkompilasi ke bytecode Java untuk dijalankan di mesin virtual Java. Ada juga PyPy tentang yang Anda dapat menulis artikel terpisah, jadi saya hanya akan menyebutkannya secara sepintas.

Pada artikel ini, kita akan fokus pada manajemen memori menggunakan alat CPython.
Catatan: Versi Python diperbarui dan apa pun bisa terjadi di masa depan. Pada saat penulisan, versi terbaru adalah Python 3.7 .

Ok, kita memiliki CPython yang ditulis dalam C yang menginterpretasikan bytecode Python. Bagaimana ini berhubungan dengan manajemen memori? Untuk mulai dengan, algoritma dan struktur untuk mengelola memori ada dalam kode CPython, di C. Untuk memahami prinsip-prinsip ini di Python, Anda memerlukan pemahaman dasar tentang CPython.

CPython ditulis dalam C, yang pada gilirannya tidak mendukung pemrograman berorientasi objek. Karena itu, kode CPython memiliki struktur yang agak menarik.

Anda pasti pernah mendengar bahwa segala sesuatu di Python adalah objek, bahkan tipe seperti int dan str, misalnya. Ini berlaku di tingkat implementasi CPython. Ada struktur yang disebut PyObject yang digunakan oleh setiap objek dalam CPython.

Catatan: Struktur dalam C adalah tipe data yang ditentukan pengguna yang mengelompokkan berbagai jenis data itu sendiri. Kita dapat menggambar analogi dengan bahasa berorientasi objek dan mengatakan bahwa struktur adalah kelas dengan atribut, tetapi tanpa metode.

PyObject adalah nenek moyang semua objek dalam Python, hanya berisi dua hal:

  • ob_refcnt : penghitung referensi;
  • ob_type : pointer ke tipe lain.

Penghitung referensi diperlukan untuk pengumpulan sampah. Kami juga memiliki pointer ke tipe objek tertentu. Jenis objek hanyalah struktur lain yang menjelaskan objek dengan Python (seperti dict atau int).

Setiap objek memiliki pengalokasi memori berorientasi objek yang tahu bagaimana mengalokasikan memori dan menyimpan objek. Setiap objek juga memiliki pembebas sumber daya berorientasi objek yang membersihkan memori jika isinya tidak lagi diperlukan.

Ada satu faktor penting dalam berbicara tentang alokasi memori dan pembersihannya. Memori adalah sumber daya bersama dari komputer, dan hal yang agak tidak menyenangkan dapat terjadi jika dua proses mencoba menulis data ke lokasi memori yang sama pada saat yang sama.

Global Interpretation Lock (GIL)

GIL adalah solusi untuk masalah umum memori bersama antara sumber daya bersama seperti memori komputer. Ketika dua utas mencoba untuk mengubah sumber daya yang sama pada saat yang sama, mereka saling menginjak. Akibatnya, kekacauan total terbentuk dalam memori dan tidak ada proses yang akan menyelesaikan pekerjaannya dengan hasil yang diinginkan.

Kembali ke analogi dengan buku, anggaplah bahwa dari dua penulis, masing-masing memutuskan bahwa ia harus menulis ceritanya di halaman saat ini pada saat tertentu. Masing-masing dari mereka mengabaikan upaya yang lain untuk menulis sebuah cerita dan mulai dengan keras kepala menulis di halaman. Akibatnya, kami memiliki dua cerita, satu di atas yang lain, dan halaman yang sama sekali tidak dapat dibaca.

Salah satu solusi untuk masalah ini adalah GIL, yang memblokir penerjemah sementara utas berinteraksi dengan sumber daya yang dialokasikan, sehingga memungkinkan satu dan hanya satu utas untuk menulis ke area memori yang dialokasikan. Ketika CPython mengalokasikan memori, ia menggunakan GIL untuk memastikan bahwa itu benar.
Pendekatan ini memiliki banyak kelebihan dan banyak kekurangan, itulah sebabnya GIL menyebabkan perselisihan di komunitas Python. Untuk mempelajari lebih lanjut tentang GIL, saya sarankan membaca artikel berikut.

Pengumpul sampah

Mari kita kembali ke analogi kita dengan buku itu dan membayangkan bahwa beberapa cerita di dalamnya sudah ketinggalan zaman. Tidak ada yang membacanya dan mengatasinya. Dalam hal ini, solusi alami adalah dengan menyingkirkan mereka sebagai tidak perlu, sehingga membebaskan ruang untuk cerita baru.
Cerita lama yang tidak digunakan seperti itu dapat dibandingkan dengan objek dalam Python yang jumlah referensinya turun menjadi 0. Ingatlah bahwa setiap objek dalam Python memiliki jumlah referensi dan penunjuk ke suatu jenis.

Jumlah referensi dapat bertambah karena beberapa alasan. Misalnya, itu akan meningkat jika Anda menetapkan satu variabel ke variabel lain.



Ini juga akan meningkat jika Anda melewatkan objek sebagai argumen.



Dalam contoh terakhir, jumlah referensi akan meningkat jika Anda memasukkan objek dalam daftar.



Python memberi tahu Anda nilai saat ini dari penghitung referensi menggunakan modul sys. Anda dapat menggunakan sys.getrefcount(numbers) , tetapi ingat bahwa memanggil getrefcount() akan menambah penghitung referensi dengan yang lain.

Bagaimanapun, jika objek dalam kode Anda masih diperlukan, nilainya untuk penghitung referensi akan lebih dari 0. Dan ketika turun ke nol, fungsi khusus akan dimulai untuk menghapus memori, yang akan membebaskannya dan membuatnya tersedia untuk objek lain.

Tapi apa artinya "membebaskan memori" dan bagaimana benda lain menggunakannya? Mari selami manajemen memori secara langsung dengan CPython.

Manajemen Memori dalam CPython

Pada bagian ini, kita akan menyelami arsitektur memori CPython dan algoritma yang digunakannya.

Seperti disebutkan sebelumnya, ada yang namanya tingkat abstraksi antara peralatan fisik dan CPython. Sistem operasi (OS) mengabstraksi memori fisik dan menciptakan tingkat memori virtual yang dapat diakses oleh aplikasi, termasuk Python.

Manajer memori virtual berorientasi OS mengalokasikan area memori tertentu untuk proses Python. Dalam gambar, area abu-abu gelap adalah ruang yang ditempati proses Python.



Python menggunakan bagian dari memori untuk penggunaan internal dan memori non-objek. Bagian lainnya dibagi menjadi penyimpanan benda ( int, dict , dll.) Sekarang saya berbicara dalam bahasa yang sangat sederhana, tetapi Anda dapat melihat langsung di bawah tenda, yaitu, ke dalam kode sumber CPython dan melihat bagaimana semuanya terjadi dari sudut pandang praktis. .

Di CPython, ada pengalokasi objek yang bertanggung jawab untuk mengalokasikan memori dalam area memori objek. Di distributor benda inilah semua sihir dilakukan. Disebut setiap kali ketika setiap objek baru perlu menempati atau membebaskan memori.

Biasanya, menambah dan menghapus data dalam Python, seperti int atau daftar, misalnya, tidak menggunakan banyak data sekaligus. Itulah sebabnya arsitektur dispenser berfokus pada bekerja dengan sejumlah kecil data per unit waktu. Juga, dia tidak mengalokasikan memori di muka, yaitu, sampai saat itu sampai menjadi mutlak diperlukan.

Komentar dalam kode sumber mendefinisikan pengalokasi sebagai "pengalokasi memori cepat tujuan khusus yang berfungsi seperti fungsi malloc universal." Dengan demikian, dalam C, malloc digunakan untuk mengalokasikan memori.

Sekarang mari kita lihat strategi alokasi memori CPython. Pertama, mari kita bicara tentang tiga bagian utama dan bagaimana mereka saling berhubungan.

Arena adalah area memori terbesar yang menempati ruang hingga batas halaman dalam memori. Batas halaman (penyebaran halaman) adalah titik ekstrem dari blok memori terus menerus dengan panjang tetap yang digunakan oleh OS. Python menetapkan batas halaman sistem ke 256 KB.



Di dalam arena adalah kolam (pool), yang dianggap sebagai satu halaman virtual memori (4 Kb). Mereka tampak seperti halaman dalam analogi kami. Kolam dibagi menjadi potongan memori yang lebih kecil.

Semua blok di kolam ditemukan dalam satu "kelas ukuran". Kelas ukuran menentukan ukuran blok, memiliki sejumlah data yang diminta. Gradasi dalam tabel di bawah ini diambil langsung dari komentar di kode sumber:



Misalnya, jika diperlukan 42 byte, maka data akan ditempatkan dalam blok 48 byte.

Kolam renang

Kolam terdiri dari balok dengan kelas ukuran yang sama. Masing-masing kelompok bekerja berdasarkan prinsip daftar tertaut ganda dengan kelompok lain dengan kelas ukuran yang sama. Oleh karena itu, algoritma dapat dengan mudah menemukan tempat yang diperlukan untuk ukuran blok yang diperlukan, bahkan di antara banyak kumpulan.

Daftar usedpools list melacak semua pool yang memiliki semacam ruang kosong yang tersedia untuk data setiap kelas ukuran. Ketika ukuran blok yang diminta diminta, algoritme memeriksa daftar kumpulan yang digunakan untuk menemukan kumpulan yang cocok untuknya.

Kolam ada di tiga negara: digunakan, penuh, kosong. Kumpulan yang digunakan berisi blok di mana beberapa informasi dapat ditulis. Blok-blok kumpulan lengkap semuanya didistribusikan dan sudah berisi data. Kolam kosong tidak mengandung data dan dapat dipecah menjadi kelas ukuran apa yang cocok jika perlu.

Daftar kumpulan kosong ( freepools list ) masing-masing berisi semua kumpulan dalam keadaan kosong. Tetapi pada titik apa mereka digunakan?

Katakanlah kode Anda membutuhkan area memori 8 byte. Jika tidak ada kolam dalam daftar kolam yang digunakan dengan ukuran kelas 8 byte, maka kolam kosong baru diinisialisasi sebagai menyimpan blok 8 byte. Kemudian kolam kosong ditambahkan ke daftar kolam yang digunakan dan dapat digunakan dalam pertanyaan berikut.

Kumpulan lengkap membebaskan beberapa blok saat informasi di dalamnya tidak lagi diperlukan. Kumpulan ini akan ditambahkan ke daftar yang digunakan sesuai dengan kelas ukurannya. Anda dapat mengamati bagaimana kumpulan mengubah status mereka dan bahkan kelas berdasarkan algoritma.

Blok



Seperti dapat dilihat dari gambar, kolam berisi pointer ke blok memori bebas. Ada sedikit nuansa dalam pekerjaan mereka. Menurut komentar dalam kode sumber, distributor "berusaha untuk tidak pernah menyentuh area memori di level mana pun (arena, kolam, blok) sampai diperlukan."

Ini berarti bahwa satu blok dapat memiliki tiga status. Mereka dapat didefinisikan sebagai berikut:

  • Untouched : area memori yang belum dialokasikan;
  • Gratis : area memori yang dialokasikan tetapi kemudian dibebaskan oleh CPython karena tidak mengandung informasi yang relevan;
  • Terdistribusi : area memori yang saat ini berisi informasi saat ini.

Pointer freeblock adalah daftar blok memori bebas yang ditautkan tunggal. Dengan kata lain, ini adalah daftar tempat-tempat gratis tempat Anda dapat menulis informasi. Jika lebih banyak memori diperlukan daripada yang ada di blok gratis, maka pengalokasi menggunakan blok yang tidak tersentuh di kumpulan.

Segera setelah manajer memori membebaskan blok, blok ini ditambahkan ke bagian atas daftar blok gratis. Daftar aktual mungkin tidak mengandung urutan terus menerus dari blok memori, seperti pada angka "sukses" pertama.



Arena

Arena berisi kolam. Arena, tidak seperti kolam renang, tidak memiliki divisi negara eksplisit.

Mereka sendiri diatur ke dalam daftar yang ditautkan ganda yang disebut daftar arena yang dapat digunakan (usable_arenas). Daftar ini diurutkan berdasarkan jumlah kolam gratis. Semakin sedikit kolam gratis, semakin dekat arena ke atas daftar.



Ini berarti bahwa arena paling lengkap akan dipilih untuk merekam lebih banyak data. Tapi mengapa tepatnya? Mengapa tidak menulis data ke tempat di mana ruang paling bebas berada?

Ini membawa kita pada gagasan membebaskan memori sepenuhnya. Faktanya adalah bahwa dalam beberapa kasus, ketika memori dibebaskan, masih tetap tidak dapat diakses oleh sistem operasi. Proses Python membuatnya tetap didistribusikan dan menggunakannya nanti untuk data baru. Deallokasi memori penuh mengembalikan memori ke sistem operasi.

Arena bukan satu-satunya area yang bisa dikosongkan sepenuhnya. Dengan demikian, kami memahami bahwa arena-arena yang ada dalam daftar "lebih dekat dengan kosong" harus dibebaskan. Dalam hal ini, area memori benar-benar dapat dibebaskan sepenuhnya, dan karenanya, total kapasitas memori program Python Anda berkurang.

Kesimpulan

Manajemen memori adalah salah satu bagian terpenting dalam bekerja dengan komputer. Dengan satu atau lain cara, Python melakukan hampir semua operasinya dalam mode siluman.

Dari artikel ini Anda belajar:

  • Apa manajemen memori dan mengapa itu penting?
  • Apa itu CPython, implementasi dasar Python;
  • Bagaimana struktur data dan algoritma bekerja dalam manajemen memori CPython dan menyimpan data Anda.

Python meringkas banyak nuansa bekerja dengan komputer. Hal ini memungkinkan untuk bekerja pada level yang lebih tinggi dan menghilangkan sakit kepala pada topik di mana dan bagaimana byte dari program Anda disimpan.

Jadi kami belajar tentang manajemen memori dengan Python. Secara tradisional, kami menunggu komentar Anda, dan kami juga mengundang Anda ke hari terbuka di kursus Pengembang Python, yang akan berlangsung pada 13 Maret

Source: https://habr.com/ru/post/id443312/


All Articles