Mundarija (20)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. getsizeof nima qaytaradi
- 2.2. Asosiy turlar (CPython 3.14, 64 bit)
- 2.3. Konteynerlar
- 2.4. Chuqur hajm
- 2.5. tracemalloc
- 2.6. Python 3.13+ obyektlari
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Turlar bo'yicha getsizeof
- Misol 2 — Chuqur hajm va tracemalloc
- Misol 3 — Obyekt nusxalari va massivlar: getsizeof aldaydi
- Misol 4 — Amaliy: xotira byudjetli kesh
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
11.8-dars: sys.getsizeof va haqiqiy hajm
11-QISM — OBYEKT MODELI VA XOTIRA · 8-dars
1. Kirish va motivatsiya
Dasturchi foydalanuvchi sessiyalarini xotiradagi keshda saqlashga qaror qildi va "qancha joy oladi" deb tekshirdi:
>>> sys.getsizeof(kesh)
8_388_696 # ~8 MB — hech gap emasServis ishga tushdi, bir soatdan keyin konteyner 2 GB chegarasiga urilib o'ldi (OOM). Sabab: sys.getsizeof lug'atning o'zini — kalit va qiymatlarga ko'rsatkichlar jadvalini — o'lchadi. Jadvaldagi har bir sessiya lug'ati, uning ichidagi satrlar, ro'yxatlar va obyektlar hisobga kirmadi. Haqiqiy hajm 1.7 GB edi.
sys.getsizeof — sayoz o'lchov: obyektning o'ziga ajratilgan xotira, u havola qilgan obyektlarsiz. Haqiqiy hajmni bilish uchun obyektlar grafini aylanib chiqish yoki ajratilgan xotirani to'g'ridan-to'g'ri kuzatish (tracemalloc) kerak.
Bu darsda:
-
getsizeofnimani o'lchaydi, nimani o'lchamaydi - Asosiy turlarning hajmi:
int,float,str(to'rt xil ko'rinish),bytes - Konteynerlar: ko'rsatkichlar jadvali va zaxira joy (over-allocation)
- Chuqur hajm: rekursiv hisoblash va uning tuzoqlari
tracemallocbilan haqiqiy ajratilgan xotirani o'lchash- Python 3.13+ obyektlari: atributlar
getsizeofga kirmaydi - Amaliy: xotira byudjeti bilan ishlaydigan kesh
2. Nazariya — chuqur tushuntirish
2.1. getsizeof nima qaytaradi
sys.getsizeof(obj) # baytlarda
sys.getsizeof(obj, standart) # hajmni aniqlab bo'lmasa — standart qiymatIchkarida:
getsizeof(obj) = type(obj).__sizeof__(obj) + GC sarlavhasi (agar obyekt GC kuzatadigan turda bo'lsa)| Kiradi | Kirmaydi |
|---|---|
| Obyekt sarlavhasi: havolalar sanog'i, tur ko'rsatkichi | Havola qilingan obyektlar (ro'yxat elementlari, lug'at kalitlari va qiymatlari) |
| O'z ichidagi ma'lumot: satr belgilari, son raqamlari | 3.13+ oddiy obyektlarning atribut qiymatlari |
| Konteynerning ko'rsatkichlar jadvali va zaxira joyi | Tur (klass) obyekti |
| GC sarlavhasi (konteynerlar uchun, 64 bitda 16 bayt) | Xotira ajratuvchining yumaloqlashi va fragmentatsiyasi |
O'z klassingizda __sizeof__ ni qayta aniqlash mumkin — getsizeof unga GC sarlavhasini qo'shadi. Manfiy qiymat — ValueError.
2.2. Asosiy turlar (CPython 3.14, 64 bit)
| Tur | Hajm | Izoh |
|---|---|---|
int |
28 bayt, har 30 bitlik "raqam"ga +4 | 0, True, 2**30 - 1 → 28; 2**30 → 32; 10**100 → 72 |
float |
24 | Har doim |
complex |
32 | |
bytes |
33 + n | |
str ASCII |
41 + n | Har belgi 1 bayt |
str latin-1 (é, ñ) |
57 + n | Har belgi 1 bayt, sarlavha kattaroq |
str UCS-2 (ş, ğ, €) |
58 + 2n | Har belgi 2 bayt |
str UCS-4 (emoji) |
60 + 4n | Har belgi 4 bayt |
range |
48 | Uzunlikdan qat'i nazar |
| Generator | ~200 | Qancha element berishidan qat'i nazar |
Satr hajmi uning eng "katta" belgisiga bog'liq (PEP 393): 1000 ta ASCII harfli satrga bitta emoji qo'shilsa, u taxminan to'rt barobar kattalashadi.
2.3. Konteynerlar
Konteyner faqat ko'rsatkichlarni saqlaydi — har biri 8 bayt:
list = 56 + 8 × sig'im (sig'im ≥ uzunlik)
tuple = 48 + 8 × uzunlik (() → 48, (1,) → 56, (1, 2) → 64) — zaxirasiz
dict = 64 (bo'sh), 1 elementdan 184, keyin sakrab o'sadi
set = 216 (bo'sh), keyin sakrab o'sadiZaxira joy (over-allocation). append har safar qayta ajratmasligi uchun ro'yxat oldindan kattaroq joy oladi (6.4-dars):
| Uzunlik | 0 | 1–4 | 5–8 | 9–16 | 17–24 | 25–32 |
|---|---|---|---|---|---|---|
getsizeof |
56 | 88 | 120 | 184 | 248 | 312 |
Shuning uchun bir xil elementli ikki ro'yxat turli hajmda bo'lishi mumkin: [0] * 10 (136) va append bilan to'ldirilgan ro'yxat (184).
getsizeof([x] * 1000) bilan getsizeof([1000 xil satr]) deyarli bir xil — elementlar o'lchamiga qaramaydi.
2.4. Chuqur hajm
"Obyekt va unga bog'liq hamma narsa" hajmi — obyektlar grafini aylanib chiqish:
def chuqur_hajm(obj, korilgan):
if id(obj) in korilgan:
return 0
korilgan.add(id(obj))
hajm = sys.getsizeof(obj)
... ichidagi obyektlar uchun rekursiyaTuzoqlar:
| Tuzoq | Yechim |
|---|---|
Bir obyektga ko'p havola ([x] * 1000) |
id bo'yicha ko'rilganlar to'plami |
| Sikllar (11.5-dars) | Xuddi shu — ko'rilganlar to'plami |
Umumiy obyektlar: kichik sonlar, None, interned satrlar |
O'lmaslarni (sys._is_immortal) va kerak bo'lsa interned satrlarni (sys._is_interned) hisobga olmaslik — 3.14 da interned satrlar o'lmas emas (Misol 4) |
| Klasslar, modullar, funksiyalar | Ularga kirmaslik — aks holda butun dastur hisoblanadi |
vars(obj) chaqiruvi 3.13+ da __dict__ ni yaratib yuboradi |
O'lchash obyektni o'zgartirishi mumkinligini bilish |
| C kengaytma obyektlari (NumPy) | O'zining __sizeof__ / nbytes |
"Kimga tegishli" savoli ham noaniq: ikki ro'yxat bitta satrni ulashsa, u qaysi biriga hisoblanadi? Chuqur hajm — taxmin, aniq javob emas.
2.5. tracemalloc
tracemalloc Python xotira ajratuvchisining har bir ajratishini kuzatadi:
tracemalloc.start()
malumot = yukla()
joriy, eng_yuqori = tracemalloc.get_traced_memory()
tracemalloc.stop()getsizeof |
chuqur hajm | tracemalloc |
|
|---|---|---|---|
| Nimani o'lchaydi | Bitta obyekt | Obyektlar grafi | Kod bajarilgan vaqtda ajratilgan hamma narsa |
| Ulashilgan obyektlar | — | Bir marta | Faqat yangi yaratilganlar |
| Yashirin joy (3.13+ atributlar, keshlar) | Qisman | ||
| Narxi | Juda arzon | Graf hajmiga bog'liq | Kodni 1.5–3 barobar sekinlashtiradi |
| Qayerda | Kodda, tez taxmin | Byudjet, monitoring | Tahlil, testlar |
tracemalloc ham operatsion tizim ko'radigan jarayon hajmi (RSS) emas: bo'shatilgan xotira OS ga darhol qaytmaydi, C kutubxonalarining o'z ajratishlari kirmaydi (29-qism).
2.6. Python 3.13+ obyektlari
Oddiy klass nusxasining atributlari 3.13 dan boshlab alohida __dict__ lug'atida emas, obyektning o'zida ("inline values") saqlanadi — __dict__ faqat so'ralganda yaratiladi. getsizeof bu qismni ko'rsatmaydi:
| 100 000 nusxa, 2 atribut | getsizeof |
tracemalloc bo'yicha bir nusxa |
|---|---|---|
| Oddiy klass | 48 | ~88 |
@dataclass |
48 | ~88 |
__slots__ |
48 | ~48 |
namedtuple |
64 | ~72 |
dict |
184 | ~184 |
Aniq raqamlar versiyaga bog'liq — xulosa: obyektlar hajmini getsizeof bilan solishtirmang, tracemalloc bilan o'lchang. __slots__ tafsilotlari — 11.9-darsda.
3. Tez ma'lumotnoma
O'lchov vositalari
sys.getsizeof(obj) → sayoz: obyektning o'zi (+ GC sarlavhasi)
obj.__sizeof__() → GC sarlavhasisiz
chuqur_hajm(obj) → graf bo'yicha taxmin (ko'rilganlar, o'lmaslarsiz)
tracemalloc → haqiqiy ajratilgan xotiraHajmlar (3.14, 64 bit)
int 28 (+4 har 30 bit) float 24 bytes 33+n
str: ASCII 41+n, latin-1 57+n, UCS-2 58+2n, UCS-4 60+4n
list 56 + 8×sig'im tuple 48 + 8×n dict 64 / 184+ set 216+
range 48 generator ~200Qoidalar
konteyner getsizeof → faqat ko'rsatkichlar
bitta emoji → butun satr 4 bayt/belgi
obyekt nusxalari → tracemalloc bilan4. Batafsil misollar
Misol 1 — Turlar bo'yicha getsizeof
"""int raqamlari, satrning to'rt ko'rinishi, konteynerlar va zaxira joy, range/generator, GC sarlavhasi."""
import sys
g = sys.getsizeof
print("=== 1. int: har 30 bitga 4 bayt ===")
for n in [0, 1, 2**30 - 1, 2**30, 2**60 - 1, 2**60, 10**100]:
print(f" {n.bit_length():>3} bit → {g(n)} bayt")
print("\n=== 2. str: eng katta belgi hamma belgi hajmini belgilaydi ===")
namunalar = {
"ASCII": "Toshkent",
"latin-1": "Montréal",
"UCS-2": "Qarşı ş",
"UCS-4": "Salom 🙂",
}
for tur, s in namunalar.items():
print(f" {tur:8} {s!r:12} uzunlik {len(s)}, hajm {g(s)}, bo'sh satrga nisbatan +{g(s) - g('')}")
matn = "a" * 1000
print(f" 1000 ta 'a': {g(matn)} + bitta emoji: {g(matn + '🙂')} ({g(matn + '🙂') / g(matn):.1f} barobar)")
print("\n=== 3. Konteynerlar: sig'im va zaxira ===")
royxat = []
oldingi = g(royxat)
qadamlar = [f"0→{oldingi}"]
for i in range(1, 33):
royxat.append(i)
if g(royxat) != oldingi:
oldingi = g(royxat)
qadamlar.append(f"{i}→{oldingi}")
print(f" append bilan o'sish (uzunlik→bayt): {', '.join(qadamlar)}")
oniga = []
for i in range(10):
oniga.append(i)
print(f" [0] * 10: {g([0] * 10)}, list(range(10)): {g(list(range(10)))}, 10 marta append: {g(oniga)}")
print(f" tuple: () {g(())}, (1,) {g((1,))}, (1, 2) {g((1, 2))}")
print(f" dict: {{}} {g({})}, 1 ta {g({'a': 1})}, 5 ta {g(dict.fromkeys(range(5)))}, 6 ta {g(dict.fromkeys(range(6)))}")
print(f" set: set() {g(set())}, 4 ta {g(set(range(4)))}, 5 ta {g(set(range(5)))}")
print("\n=== 4. Elementlar hisobga kirmaydi ===")
kichik = [1, 2, 3]
katta = ["x" * 1_000_000, "y" * 1_000_000, "z" * 1_000_000]
print(f" [1, 2, 3]: {g(kichik)}, 3 ta millionlik satr ro'yxati: {g(katta)}")
print(f" range(10): {g(range(10))}, range(10**12): {g(range(10**12))}")
print(f" generator 10 ta: {g(x for x in range(10))}, 10**9 ta: {g(x for x in range(10**9))}")
print("\n=== 5. GC sarlavhasi va __sizeof__ ===")
for obj in [[], {}, (1, 2), 12345, "salom"]:
print(f" {type(obj).__name__:6} getsizeof {g(obj):>3}, __sizeof__ {obj.__sizeof__():>3}, farq {g(obj) - obj.__sizeof__()}")
class Tasvir:
"""C kengaytma kabi: ma'lumotni o'zi boshqaradi va hajmini o'zi aytadi."""
def __init__(self, en, boy):
self._piksellar = bytearray(en * boy * 4)
def __sizeof__(self):
return object.__sizeof__(self) + sys.getsizeof(self._piksellar)
print(f" o'z __sizeof__: Tasvir(100, 100) → {g(Tasvir(100, 100))} bayt")Natijaning muhim qismi:
=== 1. int: har 30 bitga 4 bayt ===
0 bit → 28 bayt
1 bit → 28 bayt
30 bit → 28 bayt
31 bit → 32 bayt
60 bit → 32 bayt
61 bit → 36 bayt
333 bit → 72 bayt
=== 2. str: eng katta belgi hamma belgi hajmini belgilaydi ===
ASCII 'Toshkent' uzunlik 8, hajm 49, bo'sh satrga nisbatan +8
latin-1 'Montréal' uzunlik 8, hajm 65, bo'sh satrga nisbatan +24
UCS-2 'Qarşı ş' uzunlik 7, hajm 72, bo'sh satrga nisbatan +31
UCS-4 'Salom 🙂' uzunlik 7, hajm 88, bo'sh satrga nisbatan +47
1000 ta 'a': 1041 + bitta emoji: 4064 (3.9 barobar)
=== 3. Konteynerlar: sig'im va zaxira ===
append bilan o'sish (uzunlik→bayt): 0→56, 1→88, 5→120, 9→184, 17→248, 25→312
[0] * 10: 136, list(range(10)): 136, 10 marta append: 184
tuple: () 48, (1,) 56, (1, 2) 64
dict: {} 64, 1 ta 184, 5 ta 224, 6 ta 352
set: set() 216, 4 ta 216, 5 ta 728
=== 4. Elementlar hisobga kirmaydi ===
[1, 2, 3]: 88, 3 ta millionlik satr ro'yxati: 80
range(10): 48, range(10**12): 48
generator 10 ta: 200, 10**9 ta: 200
=== 5. GC sarlavhasi va __sizeof__ ===
list getsizeof 56, __sizeof__ 40, farq 16
dict getsizeof 64, __sizeof__ 48, farq 16
tuple getsizeof 64, __sizeof__ 48, farq 16
int getsizeof 28, __sizeof__ 28, farq 0
str getsizeof 46, __sizeof__ 46, farq 0
o'z __sizeof__: Tasvir(100, 100) → 40105 baytNima ko'rsatdi: 2.1–2.3-bo'limlar.
Misol 2 — Chuqur hajm va tracemalloc
"""Rekursiv chuqur hajm: ulashilgan obyektlar, sikllar, o'lmas obyektlar; natijani tracemalloc bilan solishtirish."""
import sys
import tracemalloc
import types
O_TKAZIB_YUBORISH = (type, types.ModuleType, types.FunctionType, types.BuiltinFunctionType)
def chuqur_hajm(obj, korilgan=None):
if korilgan is None:
korilgan = set()
if id(obj) in korilgan or isinstance(obj, O_TKAZIB_YUBORISH) or sys._is_immortal(obj):
return 0
korilgan.add(id(obj))
hajm = sys.getsizeof(obj)
if isinstance(obj, (str, bytes, bytearray, int, float, complex)):
return hajm
if isinstance(obj, dict):
hajm += sum(chuqur_hajm(k, korilgan) + chuqur_hajm(v, korilgan) for k, v in obj.items())
elif isinstance(obj, (list, tuple, set, frozenset)):
hajm += sum(chuqur_hajm(el, korilgan) for el in obj)
for nom in getattr(type(obj), "__slots__", ()):
if hasattr(obj, nom):
hajm += chuqur_hajm(getattr(obj, nom), korilgan)
if hasattr(obj, "__dict__"):
hajm += chuqur_hajm(vars(obj), korilgan)
return hajm
print("=== 1. Sayoz va chuqur ===")
satr = "".join(["a"] * 1000)
bir_xil = [satr] * 1000
turli = ["".join(["a"] * 1000) for _ in range(1000)]
print(f" getsizeof: bir xil satrlar {sys.getsizeof(bir_xil):>9,} turli satrlar {sys.getsizeof(turli):>9,}")
print(f" chuqur hajm: bir xil satrlar {chuqur_hajm(bir_xil):>9,} turli satrlar {chuqur_hajm(turli):>9,}")
print("\n=== 2. Sikl va o'lmas obyektlar ===")
tugun = {"nom": "".join(["ild", "iz"]), "bolalar": []}
tugun["bolalar"].append(tugun)
print(f" o'ziga havola qilgan lug'at: {chuqur_hajm(tugun)} bayt (cheksiz rekursiya yo'q)")
sonlar = list(range(200))
print(f" 0..199 ro'yxati: getsizeof {sys.getsizeof(sonlar)}, chuqur {chuqur_hajm(sonlar)} — kichik sonlar umumiy, hisoblanmadi")
katta_sonlar = [10**6 + i for i in range(200)]
print(f" 10**6.. ro'yxati: getsizeof {sys.getsizeof(katta_sonlar)}, chuqur {chuqur_hajm(katta_sonlar)} = {sys.getsizeof(katta_sonlar)} + 200 × {sys.getsizeof(10**6)}")
print("\n=== 3. Chuqur hajm va tracemalloc ===")
def sessiyalar_yarat(n):
return {
f"sess-{i:06d}": {
"foydalanuvchi": f"user{i}@wisar.uz",
"rollar": ["o'quvchi", "".join(["muallif", str(i % 3)])],
"savat": [{"id": 10_000 + j, "narx": 49_900.0 + j} for j in range(i % 4)],
}
for i in range(n)
}
tracemalloc.start()
kesh = sessiyalar_yarat(20_000)
haqiqiy, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
taxmin = chuqur_hajm(kesh)
print(f" getsizeof(kesh): {sys.getsizeof(kesh) / 1e6:6.2f} MB")
print(f" chuqur hajm: {taxmin / 1e6:6.2f} MB")
print(f" tracemalloc: {haqiqiy / 1e6:6.2f} MB")
print(f" getsizeof haqiqiyning {sys.getsizeof(kesh) / haqiqiy:.0%} ini ko'rsatdi; chuqur hajm {taxmin / haqiqiy:.0%} ini")Natijaning muhim qismi:
=== 1. Sayoz va chuqur ===
getsizeof: bir xil satrlar 8,056 turli satrlar 8,856
chuqur hajm: bir xil satrlar 9,097 turli satrlar 1,049,856
=== 2. Sikl va o'lmas obyektlar ===
o'ziga havola qilgan lug'at: 366 bayt (cheksiz rekursiya yo'q)
0..199 ro'yxati: getsizeof 1656, chuqur 1656 — kichik sonlar umumiy, hisoblanmadi
10**6.. ro'yxati: getsizeof 1656, chuqur 7256 = 1656 + 200 × 28
=== 3. Chuqur hajm va tracemalloc ===
getsizeof(kesh): 0.42 MB
chuqur hajm: 17.40 MB
tracemalloc: 17.52 MB
getsizeof haqiqiyning 2% ini ko'rsatdi; chuqur hajm 99% iniNima ko'rsatdi: 2.4, 2.5-bo'limlar.
Misol 3 — Obyekt nusxalari va massivlar: getsizeof aldaydi
"""100 000 nusxa: oddiy klass, dataclass, slots, namedtuple, tuple, dict; float ro'yxati va array.array."""
import array
import gc
import sys
import tracemalloc
from collections import namedtuple
from dataclasses import dataclass
class Nuqta:
def __init__(self, x, y):
self.x = x
self.y = y
@dataclass
class NuqtaDC:
x: float
y: float
class NuqtaSlots:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
NuqtaNT = namedtuple("NuqtaNT", "x y")
N = 100_000
X, Y = 1.5, 2.5
def bir_nusxa(yasovchi):
gc.collect()
tracemalloc.start()
nusxalar = [yasovchi() for _ in range(N)]
jami, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
return sys.getsizeof(nusxalar[0]), (jami - sys.getsizeof(nusxalar)) / N
print("=== 1. Bitta nusxa: getsizeof va haqiqiy ===")
print(f" {'tur':12} {'getsizeof':>9} {'tracemalloc':>12}")
for nom, yasovchi in [("klass", lambda: Nuqta(X, Y)), ("dataclass", lambda: NuqtaDC(X, Y)),
("__slots__", lambda: NuqtaSlots(X, Y)), ("namedtuple", lambda: NuqtaNT(X, Y)),
("tuple", lambda: tuple([X, Y])), ("dict", lambda: {"x": X, "y": Y})]:
sayoz, haqiqiy = bir_nusxa(yasovchi)
print(f" {nom:12} {sayoz:>9} {haqiqiy:>12.0f}")
print("\n=== 2. __dict__ so'ralganda nima bo'ladi ===")
nuqtalar = [Nuqta(X, Y) for _ in range(N)]
gc.collect()
tracemalloc.start()
for n in nuqtalar:
n.__dict__
qoshimcha, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f" {N:,} nusxada __dict__ ga murojaat: +{qoshimcha / N:.0f} bayt har biriga, getsizeof(nusxa) esa hamon {sys.getsizeof(nuqtalar[0])}")
del nuqtalar
print("\n=== 3. float ro'yxati va array.array ===")
gc.collect()
tracemalloc.start()
royxat = [float(i) for i in range(N)]
royxat_haqiqiy, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
tracemalloc.start()
massiv = array.array("d", (float(i) for i in range(N)))
massiv_haqiqiy, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f" list[float]: getsizeof {sys.getsizeof(royxat) / 1e6:.2f} MB, haqiqiy {royxat_haqiqiy / 1e6:.2f} MB")
print(f" array('d'): getsizeof {sys.getsizeof(massiv) / 1e6:.2f} MB, haqiqiy {massiv_haqiqiy / 1e6:.2f} MB")
print(f" getsizeof bo'yicha ular deyarli teng, haqiqatda ro'yxat {royxat_haqiqiy / massiv_haqiqiy:.1f} barobar katta")
print(f" sabab: ro'yxatda 8 baytli ko'rsatkich + har biri {sys.getsizeof(1.5)} baytli float obyekti; massivda 8 baytli raw qiymat")Natijaning muhim qismi:
=== 1. Bitta nusxa: getsizeof va haqiqiy ===
tur getsizeof tracemalloc
klass 48 88
dataclass 48 88
__slots__ 48 48
namedtuple 64 72
tuple 64 64
dict 184 184
=== 2. __dict__ so'ralganda nima bo'ladi ===
100,000 nusxada __dict__ ga murojaat: +64 bayt har biriga, getsizeof(nusxa) esa hamon 48
=== 3. float ro'yxati va array.array ===
list[float]: getsizeof 0.80 MB, haqiqiy 3.20 MB
array('d'): getsizeof 0.82 MB, haqiqiy 0.82 MB
getsizeof bo'yicha ular deyarli teng, haqiqatda ro'yxat 3.9 barobar katta
sabab: ro'yxatda 8 baytli ko'rsatkich + har biri 24 baytli float obyekti; massivda 8 baytli raw qiymatNima ko'rsatdi: 2.5, 2.6-bo'limlar.
Misol 4 — Amaliy: xotira byudjetli kesh
Sessiyalar keshi uchun yozuvlar sonini emas, xotira hajmini cheklaymiz. Har yozuv qo'shilganda chuqur hajm taxmini hisoblanadi; byudjetdan oshsa, eng eski yozuvlar chiqariladi. So'ng taxminni tracemalloc bilan tekshiramiz.
"""OrderedDict asosidagi LRU kesh: byudjet baytlarda, har yozuv chuqur hajm bilan; taxmin va tracemalloc solishtiriladi."""
import sys
import tracemalloc
from collections import OrderedDict
def chuqur_hajm(obj, korilgan=None, internlarni_sana=True):
if korilgan is None:
korilgan = set()
if id(obj) in korilgan or sys._is_immortal(obj):
return 0
if not internlarni_sana and isinstance(obj, str) and sys._is_interned(obj):
return 0
korilgan.add(id(obj))
hajm = sys.getsizeof(obj)
ichki = lambda x: chuqur_hajm(x, korilgan, internlarni_sana)
if isinstance(obj, dict):
hajm += sum(ichki(k) + ichki(v) for k, v in obj.items())
elif isinstance(obj, (list, tuple, set)):
hajm += sum(ichki(el) for el in obj)
return hajm
class HajmliKesh:
def __init__(self, byudjet_bayt):
self.byudjet = byudjet_bayt
self._malumot = OrderedDict()
self._hajmlar = {}
self.jami = 0
self.chiqarilgan = 0
def qoy(self, kalit, qiymat):
if kalit in self._malumot:
self._olib_tashla(kalit)
hajm = chuqur_hajm(kalit) + chuqur_hajm(qiymat)
if hajm > self.byudjet:
raise ValueError(f"{kalit!r}: bitta yozuv ({hajm} bayt) byudjetdan katta")
self._malumot[kalit] = qiymat
self._hajmlar[kalit] = hajm
self.jami += hajm
while self.jami > self.byudjet:
eski = next(iter(self._malumot))
self._olib_tashla(eski)
self.chiqarilgan += 1
def ol(self, kalit):
self._malumot.move_to_end(kalit)
return self._malumot[kalit]
def _olib_tashla(self, kalit):
del self._malumot[kalit]
self.jami -= self._hajmlar.pop(kalit)
def __len__(self):
return len(self._malumot)
def sessiya(i):
return {
"foydalanuvchi": f"user{i}@wisar.uz",
"tarix": [f"/kurs/python/{j}" for j in range(i % 20)],
"sozlamalar": {"til": "".join(["u", "z"]), "tema": "".join(["qorong'", "i"])},
}
print("=== 1. Byudjet 2 MB, 10 000 sessiya ===")
tracemalloc.start()
kesh = HajmliKesh(byudjet_bayt=2_000_000)
for i in range(10_000):
kesh.qoy(f"sess-{i:06d}", sessiya(i))
haqiqiy, _ = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f" keshda: {len(kesh):,} yozuv, chiqarilgan: {kesh.chiqarilgan:,}")
print(f" taxminiy jami: {kesh.jami / 1e6:.2f} MB (byudjet {kesh.byudjet / 1e6:.2f} MB)")
print(f" tracemalloc (kesh + ichki jadvallar): {haqiqiy / 1e6:.2f} MB")
print(f" taxmin haqiqiyning {kesh.jami / haqiqiy:.0%} i — taxmin haqiqiydan KATTA chiqdi")
print("\n=== 2. Nega katta: ulashilgan satrlar har yozuvda qayta sanaldi ===")
kalit_nomlari = ["foydalanuvchi", "tarix", "sozlamalar", "til", "tema"]
print(f" lug'at kalitlari kod konstantalari — interned: {all(sys._is_interned(k) for k in kalit_nomlari)}")
bir_yozuvda = sum(sys.getsizeof(k) for k in kalit_nomlari)
print(f" ular har yozuvda {bir_yozuvda} bayt deb sanalgan, aslida hamma yozuv uchun bitta nusxa: "
f"{bir_yozuvda * len(kesh) / 1e6:.2f} MB ortiqcha")
tuzatilgan = sum(chuqur_hajm(k, internlarni_sana=False) + chuqur_hajm(v, internlarni_sana=False)
for k, v in kesh._malumot.items())
jadvallar = sys.getsizeof(kesh._malumot) + sys.getsizeof(kesh._hajmlar)
print(f" internlarsiz taxmin: {tuzatilgan / 1e6:.2f} MB + OrderedDict va _hajmlar jadvallari {jadvallar / 1e6:.2f} MB"
f" = {(tuzatilgan + jadvallar) / 1e6:.2f} MB, haqiqiyning {(tuzatilgan + jadvallar) / haqiqiy:.0%} i")
print("\n=== 3. Haddan katta yozuv ===")
bir_xil = {"tarix": ["x" * 1000 for _ in range(3000)]}
turli = {"tarix": ["x" * 1000 + str(i) for i in range(3000)]}
print(f" 'x' * 1000 — oldindan hisoblangan bitta konstanta, 3000 havola: {chuqur_hajm(bir_xil) / 1e6:.2f} MB")
try:
kesh.qoy("ulkan", turli)
except ValueError as xato:
print(f" ValueError: {xato}")Natijaning muhim qismi:
=== 1. Byudjet 2 MB, 10 000 sessiya ===
keshda: 1,341 yozuv, chiqarilgan: 8,659
taxminiy jami: 2.00 MB (byudjet 2.00 MB)
tracemalloc (kesh + ichki jadvallar): 1.90 MB
taxmin haqiqiyning 105% i — taxmin haqiqiydan KATTA chiqdi
=== 2. Nega katta: ulashilgan satrlar har yozuvda qayta sanaldi ===
lug'at kalitlari kod konstantalari — interned: True
ular har yozuvda 240 bayt deb sanalgan, aslida hamma yozuv uchun bitta nusxa: 0.32 MB ortiqcha
internlarsiz taxmin: 1.68 MB + OrderedDict va _hajmlar jadvallari 0.18 MB = 1.86 MB, haqiqiyning 98% i
=== 3. Haddan katta yozuv ===
'x' * 1000 — oldindan hisoblangan bitta konstanta, 3000 havola: 0.03 MB
ValueError: 'ulkan': bitta yozuv (3160206 bayt) byudjetdan kattaNima ko'rsatdi: 2.4, 2.5-bo'limlar — yozuvlarni alohida o'lchaganda ulashilgan obyektlar (interned kalit nomlari) har yozuvda qayta sanaladi va taxmin oshib ketadi; ularni chiqarib, keshning o'z ichki jadvallarini qo'shganda taxmin haqiqiyga yaqinlashadi. Byudjet uchun biroz ortiqcha taxmin xavfsiz tomonda xato qiladi.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
"getsizeof(royxat) — ro'yxat va elementlari hajmi" |
Faqat ko'rsatkichlar jadvali |
| "Bir xil uzunlikdagi satrlar bir xil hajmda" | Eng katta belgiga bog'liq: 1, 2 yoki 4 bayt/belgi |
"int har doim 28 bayt" |
Har 30 bitga +4 bayt, cheksiz o'sadi |
"range(10**9) katta joy oladi" |
48 bayt — elementlarni saqlamaydi |
"getsizeof(obj) atributlarni ham o'lchaydi" |
3.13+ da inline atributlar kirmaydi |
"list[float] va array('d') bir xil" |
Ro'yxat ~4 barobar katta: har float alohida obyekt |
"tracemalloc = jarayon hajmi" |
Faqat Python ajratuvchisi; RSS boshqa |
| "Chuqur hajm — aniq qiymat" | Ulashilgan obyektlar tufayli taxmin |
6. Keng tarqalgan xatolar va yechimlari
1. Keshni getsizeof bilan o'lchash
if sys.getsizeof(kesh) > LIMIT: tozala() # ❌ elementlar hisoblanmaydiChuqur hajm taxmini yoki yozuv hajmlarini alohida yig'ish (Misol 4).
2. Chuqur hajmda ko'rilganlar to'plamisiz rekursiya
def hajm(o):
return sys.getsizeof(o) + sum(hajm(x) for x in o) # ❌ sikl — RecursionError, ulashilganlar ikki marta id bo'yicha korilgan to'plami.
3. Klass va modullarga kirib ketish
hajm += chuqur_hajm(type(obj)) # ❌ butun tur tizimi, modullar hisobga kiradi type, ModuleType, FunctionType ni o'tkazib yuborish.
4. Obyektlarni getsizeof bilan solishtirib, tuzilma tanlash
sys.getsizeof(Nuqta(1, 2)) == sys.getsizeof(NuqtaSlots(1, 2)) # 48 == 48 — "farqi yo'q" ❌ tracemalloc bilan ko'p nusxa yaratib, bir nusxaga bo'lish.
5. Katta raqamli ma'lumotni ro'yxatda saqlash
qiymatlar = [float(x) for x in oqim] # ⚠️ har element ~32 bayt
qiymatlar = array.array("d", map(float, oqim)) # ✅ 8 bayt; yoki NumPy (24-qism)6. Satrga tasodifiy emoji qo'shilishi
hisobot = "\n".join(qatorlar) + " ✅" # ⚠️ butun hisobot 4 bayt/belgi bo'ldi Katta matnli ma'lumotda belgilar to'plamini bilish; kerak bo'lsa bytes sifatida (UTF-8) saqlash.
7. tracemalloc ni yoqib qo'yib, production da unutish
tracemalloc.start() # ❌ butun servis sekinlashadi va qo'shimcha xotira oladiFaqat tahlil vaqtida yoki alohida endpoint/signal orqali qisqa muddatga.
8. __sizeof__ da GC sarlavhasini qo'shish
def __sizeof__(self):
return 16 + object.__sizeof__(self) + ... # ❌ getsizeof uni o'zi qo'shadi7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 4.12-dars (o'tilgan): satrlarning o'zgarmasligi va ichki tuzilishi
- 6.4-dars (o'tilgan): ro'yxat va xotira — zaxira joy
- 11.5-dars (o'tilgan): sikllar — chuqur hajm ularni hisobga oladi
- 11.7-dars (o'tilgan): interned satrlar va kichik sonlar — umumiy obyektlar
- 11.9-dars:
__slots__xotirani qanday tejaydi - 12.4-dars:
PyObject— obyekt sarlavhasi C darajasida - 24.1-dars: NumPy massivlari —
nbytesva raw ma'lumot - 29.9-dars: xotira optimizatsiyasi — RSS, fragmentatsiya, xotira sizishini topish
8. Eng yaxshi amaliyotlar
getsizeof— faqat bitta obyektning sayoz hajmi uchun.Konteyner hajmini chuqur hajm bilan taxmin qiling — ko'rilganlar to'plami, o'lmaslar va klasslarsiz.
Tuzilmalarni
tracemallocbilan solishtiring — ko'p nusxa, bir nusxaga bo'lish.Kesh va buferlarni yozuvlar soni emas, bayt byudjeti bilan cheklang (kerak bo'lsa).
Raqamli ma'lumot uchun
arrayyoki NumPy.Satr ko'rinishini (ASCII / UCS-4) hisobga oling.
tracemallocni qisqa muddatga yoqing.Jarayon darajasidagi xotirani alohida kuzating (RSS, konteyner metrikalari).
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
import sys
g = sys.getsizeof
1. print(g([1, 2, 3]) == g(["x" * 1000, "y", "z"]))
2. print(g(range(10)) == g(range(10**9)))
3. print(g("abc") - g("ab"))
4. print(g("şşş") - g("şş"))
5. print(g(2**30) - g(2**30 - 1))
6. royxat = []
royxat.append(1)
print(g(royxat) - g([]))
7. print(g((1, 2, 3)) < g([1, 2, 3]))
8. d = {}
bosh = g(d)
d["a"] = 1
print(g(d) > bosh)
9. class K:
def __sizeof__(self):
return 100
print(g(K()) > 100)
10. print(g(x for x in range(10)) == g(x for x in range(10**6)))
11. print(g(True) == g(1))
12. a = [0] * 100
b = a[:50]
print(g(b) < g(a))Javoblar
True— ikkala ro'yxat ham 3 ta ko'rsatkichTrue—rangefaqat start, stop, step saqlaydi1— ASCII, belgi boshiga 1 bayt2— UCS-24— ikkinchi 30 bitlik raqam32— birinchiappend4 ta joy ajratadiTrue— kortejda zaxira joy va ro'yxatdagi qo'shimcha maydonlar yo'qTrue— bo'sh lug'at jadvalsiz, birinchi element jadval yaratadiTrue—getsizeofGC sarlavhasi va obyekt oldi maydonlarini qo'shadiTrue— generator elementlarni saqlamaydiTrue—boolham bitta raqamliintTrue— kesma aniq 50 ta joy bilan yaratiladi
Vazifa 2: Xatolarni tuzating
1. def kesh_hajmi(kesh):
return sys.getsizeof(kesh)
2. def chuqur(o):
jami = sys.getsizeof(o)
if isinstance(o, (list, dict)):
for x in (o.values() if isinstance(o, dict) else o):
jami += chuqur(x)
return jami
3. def eng_yengil(yasovchilar):
return min(yasovchilar, key=lambda f: sys.getsizeof(f()))
4. class Rasm:
def __init__(self, data): self.data = data
def __sizeof__(self): return sys.getsizeof(self) + len(self.data)
5. tracemalloc.start()
malumot = yukla()
print(tracemalloc.get_traced_memory())Javoblar
1. def kesh_hajmi(kesh):
return chuqur_hajm(kesh) # elementlar va ichki obyektlar ham
2. def chuqur(o, korilgan=None):
korilgan = set() if korilgan is None else korilgan
if id(o) in korilgan:
return 0 # sikl va ulashilgan obyektlar
korilgan.add(id(o))
jami = sys.getsizeof(o)
if isinstance(o, dict):
jami += sum(chuqur(k, korilgan) + chuqur(v, korilgan) for k, v in o.items()) # kalitlar ham
elif isinstance(o, (list, tuple, set)):
jami += sum(chuqur(x, korilgan) for x in o)
return jami
3. def eng_yengil(yasovchilar, n=10_000):
def olch(f):
tracemalloc.start()
nusxalar = [f() for _ in range(n)]
jami = tracemalloc.get_traced_memory()[0]
tracemalloc.stop()
return jami
return min(yasovchilar, key=olch) # getsizeof inline atributlarni ko'rmaydi
4. class Rasm:
def __init__(self, data): self.data = data
def __sizeof__(self): return object.__sizeof__(self) + sys.getsizeof(self.data) # getsizeof(self) → cheksiz rekursiya
5. tracemalloc.start()
malumot = yukla()
print(tracemalloc.get_traced_memory())
tracemalloc.stop() # to'xtatilmasa dastur sekin qoladiVazifa 3: Hajm jadvali
Skript yozing:
intuchun 0 dan2**300gacha har 30 bitda hajmni chiqarib, formulasini tekshirsin- Satr uchun to'rt ko'rinishning har birida 0–10 uzunlikdagi hajmlarni chiqarib, sarlavha va belgi hajmini aniqlasin
list,dict,setuchun 0–100 elementgacha o'sish nuqtalarini topsin- Natijani Markdown jadvalga yozsin
Vazifa 4: To'liq chuqur_hajm
Misol 2 dagi funksiyani kengaytiring:
__slots__meros bo'lganda barcha ota klasslarning slotlarini hisobga olsindeque,array,defaultdict,bytearray,memoryviewni qo'llab-quvvatlasinvars()chaqirmasdan, 3.13+ obyektlar uchungc.get_referentsasosida ishlaydigan variant yozinghisobot=Truebo'lsa, turlar bo'yicha hajmniCounterko'rinishida qaytarsin- Natijani
tracemallocbilan 5 xil ma'lumot to'plamida solishtiring
Vazifa 5: Tuzilma tanlash
1 million yozuvli "buyurtma" (id, foydalanuvchi_id, summa, holat, vaqt) uchun:
dict, oddiy klass,dataclass,dataclass(slots=True),namedtuple,tuple, ustunli ko'rinish (har maydon uchun alohidaarray) variantlarini yarating- Har biri uchun
tracemallocbo'yicha jami xotira va yaratish vaqtini o'lchang holatmaydoninisys.internqilishning ta'sirini qo'shing (11.7-dars)- Natijani jadvalda ko'rsatib, qaysi holatda qaysi variantni tanlashni tushuntiring
Vazifa 6: Xotira monitori
XotiraMonitori kontekst menejerini yozing (10.6-dars):
- Kirishda
tracemalloc.start()va snapshot - Chiqishda ikkinchi snapshot,
compare_tobilan eng ko'p xotira ajratgan 10 ta qatorni chiqarsin - Eng yuqori (peak) qiymatni ham ko'rsatsin
- Dekorator sifatida ham ishlasin (10.8-dars)
- Misol 4 dagi keshni to'ldirish jarayoniga qo'llang
Vazifa 7: O'ylash
Java'da obyekt hajmini bilish uchun Instrumentation.getObjectSize (sayoz) va JOL kutubxonasi bor, Go'da unsafe.Sizeof kompilyatsiya vaqtida hisoblanadi, C++ da sizeof va Rust'da std::mem::size_of ham shunday. Nega statik tipli tillarda obyekt hajmi ko'pincha kompilyatsiya vaqtida ma'lum, Python'da esa hatto "chuqur hajm" ham taxmin? Bu farq xotira bo'yicha qaror qabul qilishga qanday ta'sir qiladi?
Javob
Qisqa javob: statik tipli tillarda tur ma'lumotning joylashuvini (layout) belgilaydi: struct {int32; float64} — kompilyator maydonlar hajmi va tekislashni biladi, qiymatlar ko'pincha to'g'ridan-to'g'ri (inline) saqlanadi. Python'da har bir qiymat — alohida obyekt, konteynerlar faqat ko'rsatkich saqlaydi, obyektlar ulashiladi va turlar ish vaqtida o'zgaradi. Shuning uchun "bu obyekt qancha joy oladi" savolining o'zi Python'da noaniq.
1. C++ va Rust
struct Nuqta { double x; double y; }; // sizeof(Nuqta) == 16
std::vector<Nuqta> v(1'000'000); // ~16 MB — ketma-ket joylashganstd::mem::size_of::<Nuqta>() // 16, kompilyatsiya vaqtida- Qiymat semantikasi:
Nuqtamassivda o'zi turadi, ko'rsatkich emas - Heap dagi ma'lumot (
std::string,Vec)sizeofga kirmaydi — ular ham "sayoz" (Python'dagi kabi), lekin bu aniq ko'rinadi
2. Go
unsafe.Sizeof— kompilyatsiya vaqtida, sayoz- Struct va massivlar qiymat sifatida saqlanadi; slice, map, string — sarlavha va ko'rsatkich
3. Java
- Primitivlar (
int,double) inline, obyektlar esa havola orqali — Python'ga yaqinroq Instrumentation.getObjectSize— sayoz; JOL maydonlar joylashuvi va tekislashni ko'rsatadiArrayList<Double>vadouble[]farqi — Python'dagilist[float]vaarray('d')farqining aynan o'zi- Valhalla loyihasi (qiymat klasslari) aynan shu muammoni hal qilishga qaratilgan
4. Python
| Xususiyat | Natija |
|---|---|
| Har qiymat — obyekt (sarlavha 16+ bayt) | float 24 bayt, C'da 8 |
| Konteynerlar faqat ko'rsatkich | Sayoz hajm hech narsa demaydi |
| Obyektlar ulashiladi (kichik sonlar, interned satrlar) | "Kimga tegishli" noaniq |
Dinamik atributlar, __dict__, inline values |
Hajm tur bilan aniqlanmaydi |
| C kengaytmalar | O'z __sizeof__ iga bog'liq |
5. Qarorlarga ta'siri
- Statik tillarda hajmni kodni o'qib hisoblash mumkin; Python'da o'lchash kerak (
tracemalloc) - Python'da katta ma'lumot uchun ustunli, qiymatlarni inline saqlaydigan tuzilmalarga o'tish —
array, NumPy, pandas, Arrow — C/Rust yondashuvini qaytaradi - Obyekt sonini kamaytirish (
__slots__, kortejlar, interning) — sarlavha narxini kamaytirish - Katta tizimda xotira byudjetini jarayon darajasida (RSS, konteyner chegarasi) kuzatish — hech bir til ichidagi o'lchov to'liq emas
6. Xulosa
- Statik tillarda tur maydonlar joylashuvini belgilaydi — hajm kompilyatsiyada ma'lum (sayoz)
- Python'da har qiymat obyekt, konteyner ko'rsatkich saqlaydi, obyektlar ulashiladi — chuqur hajm taxmin
- Java'dagi
ArrayList<Double>vadouble[]farqi Python'dagilistvaarrayfarqiga teng - Python'da xotira bo'yicha qaror — o'lchov asosida va raqamli ma'lumot uchun inline massivlarga o'tish orqali
Nimani mustahkamlaydi: 2.1–2.6-bo'limlar.
Xulosa
Bu darsda Python obyektlarining haqiqiy hajmini o'lchashni o'rgandik.
Eng muhim uch fikr:
getsizeof— sayoz o'lchov. U obyektning o'ziga ajratilgan xotira va GC sarlavhasini qaytaradi:int28 bayt (+4 har 30 bitga), satr eng katta belgisiga qarab 1, 2 yoki 4 bayt/belgi, ro'yxat 56 + 8 × sig'im. Konteyner elementlari, 3.13+ obyektlarning inline atributlari va klasslar hisobga kirmaydi — shuning uchungetsizeof(kesh)haqiqiy hajmning kichik qismini ko'rsatadi.Chuqur hajm — taxmin. Obyektlar grafini aylanib chiqish uchun ko'rilganlar to'plami (ulashilgan obyektlar va sikllar), o'lmas obyektlarni va klass/modullarni o'tkazib yuborish kerak. Natija byudjet va monitoring uchun yetarli, lekin "kimga tegishli" savoli tufayli aniq emas.
tracemalloc— haqiqiy ajratilgan xotira. Tuzilmalarni solishtirish uchun ko'p nusxa yaratib,tracemallocbilan o'lchang: oddiy klass nusxasigetsizeofbo'yicha 48, haqiqatda ~88 bayt;list[float]array('d')dan ~4 barobar katta.tracemallocham jarayon hajmi (RSS) emas va kodni sekinlashtiradi — qisqa muddatga yoqing.
Keyingi darsda __slots__ ni chuqur o'rganamiz: u obyekt tuzilishini qanday o'zgartiradi, xotirani qancha tejaydi, meros va weakref bilan qanday cheklovlari bor.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!