Pythonda xotira boshqaruvi (Memory management)

Xotira bu bo'sh kitobga o'xshaydi, qisqa hikoyalarga mo'ljallangan, lekin hali biror hikoya yozilmagan. Xotiradan foydalanish boshlanganda har xil avtorlar paydo bo'ladi va har bir sahifaga o'zlarining hikoyalarini yozishni boshlaydi.

Har bir avtor kitobga hikoya yozishdan oldin ehtiyot bo'lishlari kerak, yani birovning sahifasiga boshqa avtor hikoya yozib yuborishi mumkin emas, shuning uchun hikoya yozmoqchi bo'lgan yozuvchi kitob menejeri bilan bog'lanadi va u ruxsat bergan sahifaga hikoya yozadi.

Kitob ko'p yillardan beri foydalanilayotgani uchun unda keraksiz hikoyalar ko'payib ketishi tabiiy. Shunda ushbu hikoyalar boshqa yangi hikoyalar yozilishu uchun o'chirib tashlanadi.

Kitobni biz yaxlit xotira va uning varoqlarini malum bir chegaralangan miqdordagi xotira blocklarini saqlovchi soha sifatida qarayapmiz, sahifalarga hikoya yozadigan avtorlar esa kompyuterda ishga tushadigan turli dasturlardir. Avtorlar qaysi sahifaga hikoya yozishga ruxsat etilganligini belgilovchi kitob menejeri bu Xotira boshqaruvchisidir (Memory manager). Yangi xotira yaratish uchun eski hikoyalarni o'chiradigan kishi esa musorchi (Garbage collector)

Xotira boshqaruvi (Memory management)

Xotira boshqaruvi bu har bir dastur xotiradan o'qish va unga yozilishi tushuniladi. Dastur o'zi uchun malum xotira band qilishi mumkin, yoki malum xotira ishlatilmagan taqdirda uni bo'shatishi mumkin. Ushbu protsess umumiy qilib memory allocation deyiladi.

Python turlari

Asl Python C dasturlash tilida yozilgan. Python interpretastsiya jarayonida bytecode ga o'giriladi, va bu bytecode lar .pyc fayllarda yoki __ pycache __ papkasida saqlanadi. Pythonning Cpythondan boshqa ko'plab turlari borligini ham sanab o'tish joiz: Iron Python Microsoftning Common Language Runtime modulida ishlay olish uchun kompilyatsiya bo'ladi. JPython Java virtual mashenasida Python code ni Java bytecode ga kompile qilish uchun. PyPy, bu haqda alohida maqola o'qib chiqishni taklif qilar edim.

Python obyektlari

Pythonda hama narsa obyekt deb eshitgan bo'lsangiz kerak, misol uchun str malumot turi, int malumot turi, ularning barchasi obyekt. CPythonda barcha obyekt ishlatadigan PyObject degan struct bor.

Struct bu C dasturlash tilida turli malumot turlarini birlashtiruvchi strukturadir. OOP qiyosida qaraydigan bo'lsak u faqat atributlari bor lekin metodlari yo'q klassga o'xshaydi.

PyObject bu barcha Python obyektlarning eng katta bobosi hisoblanadi, va u faqat ikkita narsadan tashkil topadi.

  1. ob_refcnt:  reference count
  2. ob_type:   pointer to another object

ob_refcnt Pythonda garbage collection uchun ishlatiladi. ob_type esa boshqa bir malumot turiga havola (dict, int va h.kz)

Har bir obyektning o'zi uchun specific xotira beruvchi boshqaruvchisi bo'ladi (memory allocator), u obyektga qarab qanaqa qilib shu obyektga taaluqli malumotno xotiraga yozish uchun qanaqa qilib xotira olishni biladi, shu bilan  birga xotirani deallocation qiladigan ham manager bo'ladi, xotirga ishlatilmay qolganda uni boshqa maqsadlar uchun ozod qiladi.

Xotira ajratish va xotirani ozod qilish haqida gap ketganda bir muhim narsani esda tutish lozim. Xotira bu barcha uchun birdek bo'lishiladigan joy, shu sababli har xil xotira boshqaruvchisi bitta xotiraga yozishga harakat qilganda nojuya natijalarga olib kelishi mumkin.

GIL Global interpreter lock

Pythonda thread tarqatilgan xotira ustida amal bajarayotganda, boshqa thread uning dumini bosib olmasligi uchun GIL yechim hisoblanadi. Misol uchun ikkita shoir bitta listga sher yozsa oxirgi natija tushunarsiz text bo'lib qoladi, GIL bu muammoga bitta listga bir vaqtda faqat bitta shoir sher yozoladi degan qoida bilan buning oldini oladi.

Axlat yig'ish (Garbage collection)

Yuqorida misol keltirilgan shoirlar tushunchasida misol qiladigon bo'lsak, biror varoqdagi sher endi bizga kerak bo'lmaydigan bo'lsa biz undan qutilishimiz mumkin, yani o'chirib boshqa sher yozishimiz mumkin.

O'chirib yuborilgan eski sherni biz 0 (nol) ga tushirib qo'yilgan ob_refcnt yani object reference countga o'xshatishimiz mumkin.

Pythonda ref_count har xil sabablarga ko'ra o'sib borishi mumkin, misol uchun yangi o'zgaruvchiga biriktirilsa.

numbers = [1, 2, 3]
# Reference count = 1
more_numbers = numbers
# Reference count = 2

ref_count o'zgaruvchini funksiyaga argument qilib bergan taqdirda ham o'zgaradi

total = sum(numbers)

ref_count o'zgarishiga oxirgi misol bu, agar siz o'zgaruvchini list elementlari qilib bergan taqdiringizda ref_count oshib boradi.

matrix = [numbers, numbers, numbers]

Pythonda istalgan obyektni ref_countini bilib olishingiz mumkin, bu funksiya sys modulning getrefcount() funksiyasi orqali amalga oshiriladi, lekin yodingizda bo'lsin, obyektning refcountini ko'rish uning ref_countini yana bittaga oshiradi.

Xotira boshqaruvi (Memory management)

CPythondan hardware qadar orada abstraksiya qatlamlari mavjud. OS har doim applicationlar amal bajarishlari uchun fizik xotiradan joy ajratib virtual xotira yaratadi. Quyidagi to'q kulrang sohalar Python uchun mo'ljallangan xotiradir.

Cpythonning object allocatori object memory sohasidan xotira ajratish uchun javobgardir. Ushbu yashil object allocator soha asosiy mo'jiza yuz beradigon yerdir. U har doim pythonda yangi object yaratilayotganda yoki o'chirilayotganda ishga tushadi.

Pythonning source codiga berilgan izohlarda object allocatorga "umumiy malloc ustida ishlovchi va kichik hajmdagi blocklar uchun qulay" deyiladi. Bu yerda malloc C ning xotira boshqaruvi bibliotekasining funksiyasi hisoblanadi.

Hozir CPythonning xotira boshqaruvi strategiyasidagi 3 ta asosiy aspektga etibor beramiz.

Arena bu xotiradagi bir varoq deb hisoblangan Soha chegarasidir (page boundary). Soha chegarasi (page boundary) bu malum hajmdagi birlik deb hisoblangan va OS foydalanadigan xotiradagi bir bo'shliq. Python xotiradagi sohani 256 kb deb tasavvur qiladi.

Sohalar orasida havzalar (pool) lar mavjud, ular virtual xotirada 4 kb hisoblanadi. Kitob qiyosida oladigan bo'lsak bitta pool bir varoq degani. Har bir pool yanada kichikroq xotira blocklariga bo'linadi.

Pooldagi har bir blockning o'zining size class i bo'ladi. Bar bir size class mahsus block hajmini bildiradi, maxsus block hajmi so'ralayotgan bosh xotira hajmiga bog'liq, Pythonning source codida aynan ushbu izohlar qancha hajm egallaydigan object qanaqa blockga tushishi jadvali keltirilgan.

Misol uchun, 42 byte hajmdagi obyektni saqlamoqchi bo'lsa, python bizga 48 byteli blockga yo'naltiradi.

Poollar bir xil size class dan yaratilgan xotira blocklaridir. Har bir pool o'zida o'xshash size classga tegishli boshqa pool uchun double linked lists saqlaydilar (qo'shaloq ulanganlik havolasi).  Shu usulda Python so'ralayotgan xotirani osongina bir nechta pool bo'ylab izlab topishi mumkin.

usedpools har bir xotira blocki uchun ishlatilgan lekin bo'sh joylari bor xotira blocklari listini o'zida saqlaydi. Poollar 3 ta holatda bo'lishlari mumkin: used, full, yoki empty. used ishlatilgan lekin bo'sh orin bor, full bo'sh o'rin yoq, empty ishlatilmagan.

freepools ro'yhati ishlatilmagan poollarni ro'yhatini o'z ichida saqlaydi, lekin qachon poollari ishlatilmasdan qoladi, yoki bo'shatiladi ?

Tasavur qiling, siz 8 byte lik xotira so'radingiz, biror usedpoolda uncha xotira topilmadi, shunda biz freepoollardan birortasini olamiz va unga 8 byte lik xotira blockini yaratib malumotni saqlaymiz, shunda ushbu pool usedpoolss listiga qo'shiladi, keyingi xotira allocationida ishlatiladi.

Yoki biror fullpool ning biror blocki bo'shasa, ushbu block boshqa kerak bo'lmaganligi sababli, u pool o'z size classiga qarab usedpoolsga qo'shiladi.

Diagrammada ko'rib turganingizdek poolning bo'sh xotira blocklariga pointeri qaratilgan bo'ladi. Bu sturkturaning ishlash prinsipida biroz yetishmovchilik bor. Pointer uch qatlam bo'yicha ishlaydi (arena, pool, block) va u haqiqatdan kerak bo'lmasa xotira blockini ishlatmaydi.

Poolning blocklarida 3 ta holat bo'ladi:

  • Untouched: Hali allocatsiya qilinmagan xotira blocki
  • Free: Allocation qilingan lekin keyinchalik CPython uni bo'shatgan, va hozircha kerakli malumot o'zida saqlamayatkan block
  • Allocated: O'zida kerakli malumot saqlab turgan xotira blocki

Freeblock pointeri doim ishlatilmayotgan yani biror malumot saqlasa bo'ladigan blocklarga qaratilgan bo'ladi. Agar so'ralayotgan xotira freeblocklarda yetarli bo'lmasa, allocator pooldagi untouched blocklardan kerakli miqdorda oladi.

Qachonki xotira menejeri biror xotira blockini bo'shatsa ushbu block freeblocks listga qo'shib qo'yiladi. Freeblocklar tepadagi diagrammaga o'xshab ketma ket yopishib turmasligi ham mumkin aksincha quyidagi diagrammada ko'rsatilgandek bo'lishi mumkin:

Arenas

Arenalar poollardan tashkil topadi. Poollar uch holatda yani used, full, empty bo'lishi mumkin. Arenaning o'zida esa pool kabi malum xolat mavjud emas.

Arenalar o'zaro bog'langan list ko'rinishida qurilgan va bu list usable_arenas deb nomlanadi. Bu list foydalanilgan poollar soniga qarab sort qilingan bo'ladi, yani qancha ko'p poool ishlatilgan bo'lsa shu arena listning boshiga yaqin bo'ladi.

Bundan chiqdi xotira zarur bo'lganda eng malumotga to'la arena tanlanadi, eng malumoti kam erana emas. Buning sababi nima ? nima uchun teskarisi emas ?

Bu narsa bizni xotirani to'laqonli bo'shatish tushunchasiga olib keladi, yani xotira blocki bo'shatilganganda u to'laqonli bo'shatilmaydi, yani OS ga qaytarilmaydi, uni Python allocation xolatida saqlaydi. Xotira to'laqonli bo'shatilsa u OS ga qaytarilishi kerak.

To'laqonli bo'shatilishi mumkin bo'lgan narsa bu faqat Arenadir. usable_arenas listni maqsadi shuki bo'sh xolatga yaqin bo'lgan arenalar bo'shatilishu zarur, shunda malum xotira to'laqonli bo'shatilishi mumkin, va Python programmasi ishlatayotgan umumiy xotira hajmi kamayadi.

Xulosa

Xotira boshqaruvi kompyuter olamining muhim integral qismi hisoblanadi. Yaxshimi yoki yomonmi Python buning barchasini parda ortida bajaradi.

Biz ushbu maqolada o'rgandik:

  • Xotira boshqaruvi nima va u nima uchun muhim ?
  • Pythonning o'z asl xolati, CPython, C dasturlash tilida yozilgan
  • CPythonda xotira boshqaruvini taminlash uchun malumotlar strukturasi va lagoritmlar birgalikda qanday ishlaydi

Python compyuter bilan ishlashning mayda detallarini abstraktsiyalashtiradi. Bu bizga yuqori qatlamdagi funksionallarni har bitta bit hardwareda qanday saqlanishi haqida qayg'urmasdan yaratishimizga imkon beradi.