aidoc.uz

6-qism · Tamoyillar va ichki mexanizm·16-bob

Dart Virtual Machine va Flutter Engine — kod qanday ishga tushadi

JIT va AOT kompilyatsiyadan tortib, isolate'lar, event loop'ning aniq ishlash tartibi va Flutter'ning to'rtta thread'igacha — ilova ekranga chiqishidan oldin nima sodir bo'lishining to'liq tahlili.

3-bobda Future, async/await, Stream va Isolate'ni qanday yozishni ko'rdik. Bu bobda esa boshqa savolga javob beramiz: yozgan kodingiz aslida qanday ishga tushadi — qaysi dastur uni o'qiydi, qaysi tilga aylantiradi, qaysi thread'da bajaradi va ekranga qanday piksellar chiqaradi. Senior darajadagi intervyularda ko'pincha aynan shu savollar "kod yoza oladigan dasturchi" bilan "platformani tushunadigan muhandis"ni ajratadi: "nega hot reload state'ni saqlab qoladi-yu, hot restart saqlamaydi?", "isolate va thread bir xil narsami?", "Flutter bitta ekranni chizish uchun nechta thread ishlatadi?" — bularning barchasi shu bobda javob topadi.

1. Dart kodining ikki yo'li: JIT va AOT#

Dart kodi ikki butunlay boshqa rejimda ishga tushirilishi mumkin, va Flutter loyihasida siz bu ikkalasini ham har kuni, bilib-bilmay ishlatasiz.

JIT (Just-In-Time) — flutter run bilan debug rejimida ishga tushirganingizda ishlatiladigan rejim. Dart VM kodni ishga tushirish paytida, kerak bo'lgan joyda mashina koduga aylantiradi (kompilyatsiya qiladi) va natijani keshlaydi. Bunda VM butun vaqt davomida ishlab turadi — aynan shu sabab hot reload mumkin: VM ishlayotgan holatda, siz o'zgartirgan qismgina qayta kompilyatsiya qilinib, ishlab turgan dastur ichiga "in'ektsiya" qilinadi.

AOT (Ahead-Of-Time) — flutter build apk/flutter build ipa bilan release build qurganingizda ishlatiladigan rejim. Butun Dart kodi build vaqtidayoq to'g'ridan- to'g'ri native mashina koduga (ARM64, x64) aylantiriladi — qurilmada ishga tushganda hech qanday interpretatsiya yoki runtime kompilyatsiya kerak emas, kod allaqachon protsessor tushunadigan formatda. Shuning uchun release build'lar tezroq ishga tushadi va barqarorroq performance ko'rsatadi — lekin buning evaziga hot reload yo'qoladi (kod allaqachon "muzlatilgan").

Dart source
.dart fayllar
Siz yozgan odam o'qiy oladigan Dart kodi.
Kernel (.dill)
Dart Kernel AST
Front-end parser Dart kodini umumiy oraliq ko'rinishga (Abstract Syntax Tree) aylantiradi — JIT ham, AOT ham shu bosqichdan o'tadi.
JIT yo'li
Runtime kompilyatsiya
Kernel kodini VM ishlayotganda, kerak bo'lgan joyda mashina koduga aylantiradi. Debug rejim, hot reload mumkin.
AOT yo'li
Build-vaqtida kompilyatsiya
Kernel kodini build paytida to'liq native mashina koduga aylantiradi. Release rejim, hot reload yo'q.

Ikkala yo'l ham bitta umumiy oraliq bosqichdan — Dart Kernel (.dill fayl) dan o'tadi. Kernel — Dart kodining tilga bog'liq bo'lmagan, daraxt shaklidagi (AST) ko'rinishi. Bu arxitektura tufayli front-end (parsing, tip tekshirish) faqat bir marta yoziladi, JIT va AOT esa shu umumiy natijadan ikki xil yo'nalishda foydalanadi.

2. Isolate'lar — nega Dart'da "thread" so'zi ishlatilmaydi#

Bu — ko'p dasturchini chalkashtiradigan joy. Java yoki C++'da bir nechta thread bir xil xotira (heap)ga kirish huquiga ega — shu sabab synchronized, mutex, lock kabi vositalar kerak bo'ladi: ikki thread bir vaqtda bir o'zgaruvchini o'zgartirsa, "race condition" yuzaga keladi.

Dart'da bunday emas. Har bir isolate — o'zining alohida xotira uyumi (heap) va o'zining alohida garbage collectoriga ega mustaqil ijro birligi. Ikki isolate o'rtasida hech qanday umumiy xotira yo'q — ular faqat xabar almashish (message passing) orqali gaplashadi: SendPort orqali yuborilgan ma'lumot nusxa ko'chiriladi (copy qilinadi), havola (reference) sifatida emas.

// Isolate yaratish va xabar almashish
void heavyWork(SendPort sendPort) {
  final result = _parseHugeJson(); // og'ir hisoblash
  sendPort.send(result); // natija NUSXA sifatida yuboriladi
}
 
Future<void> main() async {
  final receivePort = ReceivePort();
  await Isolate.spawn(heavyWork, receivePort.sendPort);
  final result = await receivePort.first;
  print('Natija: $result');
}

Aynan shu — umumiy xotira yo'qligi — Dart'da nega mutex, lock, semaphore kabi primitivlar kerak emasligining sababi: bir isolate ichidagi kod hech qachon boshqa isolate bilan bir vaqtda bitta xotira manzilini o'zgartirmaydi, chunki bunday umumiy manzil umuman mavjud emas.

Amalda qachon isolate ochiladi? Har bir Flutter ilovasi kamida bitta — root isolate (asosiy, "UI isolate" deb ham ataladi) bilan boshlanadi. Yangi isolate faqat CPU-og'ir, bloklovchi hisoblash kerak bo'lganda qo'lda ochiladi: katta JSON'ni parse qilish, rasmni qayta ishlash, kriptografik hisoblash. Oddiy tarmoq so'rovi yoki fayl o'qish uchun yangi isolate kerak emas — ular allaqachon asinxron (I/O-bound) va event loop orqali bloklamasdan kutiladi (4-bandda tushuntiramiz).

// compute() — yangi isolate ochishning qulay qobig'i
final parsed = await compute(_parseHugeJson, rawJsonString);

compute() — Isolate.spawnning qulay wrapper'i: yangi isolate ochadi, funksiyani o'sha yerda ishga tushiradi, natijani qaytaradi va isolate'ni avtomatik yopadi.

Isolate'lar VM darajasida OS thread'lariga moslashtiriladi — VM har bir isolate'ni (yoki ba'zi holatlarda bir nechtasini) alohida OS thread'ida rejalashtirishi mumkin. Lekin bu joylashtirish tafsiloti dasturchiga ko'rinmaydi va kafolatlanmaydi — muhim narsa shundaki, isolate'lar orasida hech qachon umumiy o'zgaruvchiga bir vaqtda kirish bo'lmaydi, OS qaysi thread'da ishlatishidan qat'iy nazar.

3. Event loop — aniq tartib bilan#

Root isolate — bitta thread, bitta event loop. Bu shuni anglatadiki, bitta isolate ichida haqiqiy parallellik yo'q — faqat navbat bilan almashtirish (interleaving) bor. async/await kod bloklamaydi, chunki u ishni "keyinroq davom ettirish" uchun navbatga qo'yadi, boshqa hech qanday thread ochilmaydi.

Event loop ikkita navbat bilan ishlaydi, va ular orasidagi ustuvorlik tartibi — eng ko'p intervyu savoli beriladigan joy:

  1. Microtask navbati — yuqori ustuvorlik. Future.then(), scheduleMicrotask() shu yerga tushadi.
  2. Event navbati — pastroq ustuvorlik. I/O callback'lar, timer'lar (Timer, Future.delayed), gesture hodisalari shu yerga tushadi.

Qoida: har bir event navbatidagi elementni bajarishdan OLDIN, microtask navbati TO'LIQ bo'shatiladi — hatto shu jarayonda yangi microtask qo'shilsa ham, ular ham bajarilib bo'lingunga qadar event navbatiga o'tilmaydi.

void main() {
  print('1: sinxron boshlanish');
 
  Future.delayed(Duration.zero, () => print('2: event navbati (delayed)'));
 
  Future.value().then((_) => print('3: microtask'));
 
  scheduleMicrotask(() => print('4: microtask (to\'g\'ridan-to\'g\'ri)'));
 
  print('5: sinxron tugash');
 
  // Natija tartibi:
  // 1: sinxron boshlanish
  // 5: sinxron tugash
  // 4: microtask (to'g'ridan-to'g'ri)
  // 3: microtask
  // 2: event navbati (delayed)
}

Nega shunday: avval butun sinxron kod (main()ning o'zi) oxirigacha ishlaydi — hech qanday asinxron chaqiruv sinxron kodni to'xtata olmaydi. main() tugagach, event loop microtask navbatiga qaraydi — u yerda ikkita element bor (3 va 4), ular qo'shilgan tartibda ishga tushadi. Faqat microtask navbati butunlay bo'shagandan keyin, event loop event navbatiga o'tadi va 2ni bajaradi — hatto Duration.zero bilan "kechiktirilmagan" bo'lsa ham, u baribir microtask'lardan keyin keladi, chunki u event navbatida turibdi.

4. Flutter Engine arxitekturasi — uch qatlam#

Flutter ilovasi uchta aniq ajratilgan qatlamdan iborat, har biri boshqa tilda yozilgan:

Framework (Dart)
Widget'lar, rendering mantiqi, gesture'lar, animatsiyalar — dasturchi kundalik yozadigan barcha kod shu yerda
Engine (C++)
Skia/Impeller renderlash, matn joylashuvi (text layout), Dart runtime'ning o'zi, platform channel infratuzilmasi
Embedder (platformaga xos)
Android/iOS/desktop/web uchun alohida qatlam — native oyna, kirish (input) hodisalari, plugin hayot davri

Framework — sizga tanish qism: StatelessWidget, Column, GestureDetector va hokazo, hammasi Dart'da yozilgan, ochiq manbali va o'qish mumkin. Engine — C++'da yozilgan, Skia (yoki Impeller) grafik kutubxonasi orqali haqiqiy renderlashni bajaradi, matnni joylashtiradi (harflar, shrift, qatorlarga bo'lish), va Dart VM'ning o'zini o'z ichiga oladi. Embedder — har bir platforma uchun alohida: Android'da bu Java/Kotlin qatlami, iOS'da Objective-C/Swift, desktop'da mos native oyna tizimi. Embedder — Flutter engine'ni native OS oynasiga "ulovchi" yelim qatlam: klaviatura, sichqoncha, life-cycle hodisalarini Flutter'ga uzatadi.

Bu ajratish tufayli Flutter kodi (Framework qatlami) platformalar orasida deyarli 100% bir xil qoladi — farq faqat Embedder qatlamida.

5. Bitta kadr (frame) qanday chiziladi — to'rtta thread#

"Flutter bitta ekranni chizish uchun nechta thread ishlatadi?" — juda tez-tez so'raladigan savol. Javob: to'rtta asosiy thread, har biri aniq vazifaga ega:

Platform thread
OS'ning asosiy thread'i — platform channel chaqiruvlarini, native plugin kodini bajaradi
UI thread
Chalg'ituvchi nom — bu yerda piksel chizilmaydi! Dart VM'ning root isolate'i shu yerda ishlaydi: build(), widget/element/render daraxtlari, natijada 'layer tree' hosil bo'ladi
Raster thread (GPU thread)
Layer tree'ni oladi va Skia/Impeller orqali haqiqiy piksellarga aylantiradi, GPU'ga topshiradi
IO thread
Rasm dekodlash va boshqa I/O-og'ir ishlarni UI thread'dan ajratib bajaradi, kadr sur'atini buzmasligi uchun

Platform thread — OS nuqtai nazaridan ilovaning asosiy thread'i; platform channel orqali native kodga (masalan, kamera ochish) murojaat shu yerda bajariladi. UI thread — nomiga qaramay, bu yerda hech qanday piksel chizilmaydi: bu yerda Dart VM'ning root isolate'i ishlaydi, siz yozgan build() metodlari shu yerda chaqiriladi, natijada Widget → Element → RenderObject zanjiri ishlab, "layer tree" (chizish buyruqlari ro'yxati) hosil bo'ladi. Raster thread (ba'zan "GPU thread" deb ham ataladi) — layer tree'ni oladi va uni Skia yoki Impeller orqali haqiqiy piksellarga aylantiradi, keyin GPU'ga topshiradi. IO thread — rasm fayllarini dekodlash kabi og'ir, lekin CPU-bog'liq bo'lmagan ishlarni UI thread'dan ajratib bajaradi, shunda katta rasm yuklash kadr sur'atini buzmaydi.

"Jank" (kadr tushib qolishi) qanday yuz beradi: 60 fps uchun har bir kadrga ~16ms, 120 fps uchun ~8ms vaqt bor. Agar UI thread build()/layout'da juda uzoq turib qolsa (masalan, og'ir hisoblash to'g'ridan-to'g'ri build() ichida), yoki Raster thread murakkab chizish buyruqlarini (masalan, ko'p qatlamli soyalar, blur effektlar) vaqtida rasterlay olmasa — kadr byudjeti oshib ketadi, ekran bir kadrni "yo'qotadi", foydalanuvchi buni "silliq emas" (jank) sifatida his qiladi.

6. Skia va Impeller — renderlash dvigateli#

Skia — Flutter'ning asl renderlash kutubxonasi (Chrome brauzerida ham ishlatiladi). Uning bir kamchiligi bor: yangi, oldin ko'rilmagan shakl yoki effekt (masalan, murakkab soya, maxsus ShaderMask) birinchi marta chizilganda, Skia kerakli shader kodini o'sha zahoti (runtime'da) kompilyatsiya qiladi — bu esa ko'zga tashlanadigan qisqa "sakrash" (shader compilation jank) sifatida namoyon bo'ladi, ayniqsa animatsiya paytida birinchi marta yangi effekt paydo bo'lganda.

Impeller — Flutter'ning yangi renderlash dvigateli, aynan shu muammoni yechish uchun loyihalangan: kerakli shader'larning katta qismini build vaqtida oldindan kompilyatsiya qiladi, shunda runtime'da kutilmagan "birinchi chizish sakrashi" bo'lmaydi. Impeller qaysi platformalarda standart (default) ekanligi Flutter versiyalari orasida o'zgarib boradi — intervyuda aniq versiya raqamini aytishdan ko'ra, "Impeller — shader jank muammosini yechish uchun kiritilgan, joriy holatni Flutter'ning rasmiy hujjatidan tekshirish kerak" deb javob berish xavfsizroq.

7. Garbage collection — nega ikki avlod#

Dart'ning axlat yig'uvchisi (garbage collector) avlodli (generational) strategiyaga asoslangan — bu Flutter'ning odatiy xotira ishlatish naqshiga juda mos keladi.

  • Yosh avlod (young generation) — tez, "scavenger" (nusxalovchi) kollektor bilan tozalanadi. Flutter UI kodi doimiy ravishda ko'p sonli qisqa umrli obyektlar yaratadi (har bir build() chaqiruvida yangi widget instance'lari) — bu obyektlarning aksariyati darhol keraksiz bo'lib qoladi. Yosh avlod kollektori aynan shunday "tez tug'iladi, tez o'ladi" obyektlar uchun optimallashtirilgan va juda tez ishlaydi.
  • Eski avlod (old generation) — mark-sweep (belgilash-tozalash) kollektor bilan tozalanadi, sekinroq, lekin kamroq chaqiriladi. Bir necha "scavenge" siklidan omon qolgan obyektlar (masalan, uzoq umr ko'radigan controller'lar, doimiy saqlanadigan ma'lumotlar) shu yerga "ko'chiriladi".

Bu ikki bosqichli yondashuv tufayli Flutter'ning rebuild-og'ir ishlash uslubi (har bir setState yangi widget obyektlari yaratadi) samarali boshqariladi — agar har safar butun heap tekshirilsa, bu juda sekin bo'lar edi.

8. Hot reload va hot restart — mexanik farq#

  • Hot reload: yangilangan Dart kodi ishlayotgan VM'ning o'ziga in'ektsiya qilinadi (JIT orqali) va faqat o'zgargan widget'lar uchun build() qayta chaqiriladi. Isolate o'chirilmaydi — xotiradagi holat (masalan, hozirgi matn maydoni qiymati, scroll pozitsiyasi) saqlanib qoladi.
  • Hot restart: isolate butunlay yo'q qilinadi va yangidan yaratiladi — main() boshidan ishga tushadi, barcha state yo'qoladi, lekin static o'zgaruvchilar va top-level initializer'lar ham qaytadan bajariladi.

Hot reload har doim ham ishlamaydi — masalan, enum qiymatlari o'zgarganda, generic tip deklaratsiyasi o'zgarganda yoki top-level main() funksiyasining o'zi o'zgarganda, VM ichki holatini yangi strukturaga moslay olmaydi va Flutter avtomatik ravishda hot restart talab qiladi (yoki xato ko'rsatadi).

Ko'p uchraydigan xato javoblar#

Noto'g'ri tushunchaTo'g'ri tushuntirish
"Isolate — bu Java thread'ining Dart'dagi analogi"Isolate'lar umumiy xotirani BUTUNLAY baham ko'rmaydi — Java thread'lari esa bir xil heap'ni baham ko'radi. Bu tubdan boshqa model: xabar almashish vs umumiy xotira + lock
"Future.delayed(Duration.zero) sinxron kod bilan bir vaqtda ishlaydi"Yo'q — u har doim event navbatiga tushadi, hatto nol kechikish bilan ham, va butun microtask navbatidan KEYIN bajariladi
"Debug va release build bir xil tezlikda ishlaydi, farq faqat belgilarda"Debug — JIT bilan, qo'shimcha tekshiruv va assertion'lar bilan ishlaydi; release — AOT bilan, native kodga aylantirilgan va sezilarli tezroq
"UI thread — bu yerda piksel chiziladi, shuning uchun nomi shunday"UI thread faqat widget/layout mantiqini (Dart kodi) bajaradi; haqiqiy piksel chizish Raster thread'da, Skia/Impeller orqali sodir bo'ladi
"Har qanday og'ir hisoblash uchun isolate ochish kerak"Faqat CPU-bog'liq, uzoq (odatda >16ms) ishlar uchun; I/O-bound ishlar (tarmoq, fayl) allaqachon asinxron, alohida isolate shart emas

Tez-tez so'raladigan intervyu savollari#

Savol: JIT va AOT o'rtasidagi asosiy farq nima, va nega Flutter ikkalasini ham ishlatadi?

Javob: JIT kodni runtime'da, kerak bo'lgan joyda kompilyatsiya qiladi — bu VM'ni doimiy ishlab turishini talab qiladi va shu tufayli hot reload imkonini beradi, lekin runtime'dagi kompilyatsiya narxi tufayli birinchi ishga tushish sekinroq bo'lishi mumkin. AOT butun kodni build vaqtida native mashina koduga aylantiradi — runtime'da hech qanday kompilyatsiya kerak emas, ishga tushish tezroq va performance barqarorroq, lekin hot reload imkoniyati yo'qoladi. Flutter debug paytida dasturchi tezkorligi (hot reload) uchun JIT'ni, release'da esa foydalanuvchi tajribasi (tezlik, barqarorlik) uchun AOT'ni tanlaydi — ikkalasi ham bitta umumiy Kernel oraliq ko'rinishidan boshlanadi.

Savol: Nega Dart'da mutex yoki lock kabi sinxronizatsiya primitivlari yo'q?

Javob: Chunki Dart'ning izolatsiya modeli — isolate'lar hech qachon umumiy xotirani baham ko'rmaydi. Java/C++'dagi thread'lar bir xil heap'ga kirish huquqiga ega bo'lgani uchun, ikki thread bir o'zgaruvchini bir vaqtda o'zgartirmasligi uchun lock kerak. Dart'da bunday umumiy xotira manzili umuman mavjud emas — ma'lumot faqat message passing orqali, nusxa ko'chirilgan holda uzatiladi. Shu sababdan "data race" tushunchasining o'zi Dart'ning normal isolate modelida yo'q.

Savol: compute() funksiyasi qachon ishlatiladi, va uni har bir asinxron amal uchun ishlatish nega yomon g'oya?

Javob: compute() yangi isolate ochib, unda berilgan funksiyani bajaradi va natijani qaytaradi — bu CPU-bog'liq (masalan, katta JSON parse qilish, rasmni qayta ishlash) ishlar uchun to'g'ri, chunki bunday ishlar UI isolate'ini bloklab qo'yishi mumkin. Lekin har bir tarmoq so'rovi yoki fayl operatsiyasi uchun compute() ishlatish ortiqcha — bu operatsiyalar allaqachon I/O-bound va asinxron (async/ await orqali event loop'da kutiladi), UI isolate'ini bloklamaydi. Yangi isolate ochish o'zi xotira va vaqt narxiga ega — kichik, I/O-bound ishlar uchun bu narx foydasidan ko'p.

Savol: Quyidagi kod nima chop etadi va nega?

void main() {
  Future(() => print('A'));
  Future.microtask(() => print('B'));
  print('C');
}

Javob: Natija: C, B, A. Future(() => ...) — event navbatiga tushadi (u yangi "task" sifatida rejalashtiriladi), Future.microtask(() => ...) esa microtask navbatiga tushadi. Avval main()ning sinxron qismi (print('C')) tugaydi. Keyin event loop microtask navbatini to'liq bo'shatadi — B chop etiladi. Faqat shundan keyin event navbatiga o'tiladi va A chop etiladi.

Savol: Root isolate ichida haqiqiy parallellik bormi?

Javob: Yo'q. Bitta isolate — bitta thread, bitta event loop degani. async/ await kod ijrosini to'xtatmaydi, faqat "keyinroq davom ettirish" uchun navbatga qo'yadi — ikkita async funksiya hech qachon bir vaqtda, chinakam parallel bajarilmaydi, ular faqat bir-birining orasida almashtiriladi (interleave qilinadi). Haqiqiy parallellik faqat bir nechta isolate ochilganda, ular turli OS thread'larida ishlaganda yuzaga keladi.

Savol: Flutter'ning "UI thread"i va brauzerdagi "UI thread" tushunchasi bir xilmi?

Javob: Konseptual jihatdan o'xshash (ikkalasida ham asosiy mantiq shu yerda ishlaydi), lekin Flutter'da chalkashtiruvchi nom muammosi bor: Flutter'ning "UI thread"i faqat Dart kodini (build(), layout, widget daraxti) bajaradi, piksel chizmaydi. Haqiqiy rasterlash alohida Raster thread'da bo'ladi. Ba'zi hujjatlarda "UI thread" o'rniga "root isolate thread" atamasi ham ishlatiladi — bu chalkashlikni kamaytirish uchun.

Savol: Nega Impeller kiritildi — Skia'da aniq qanday muammo bor edi?

Javob: Skia ba'zi shader'larni (grafik effektlarni renderlash uchun kichik GPU dasturchalari) birinchi marta kerak bo'lganda, runtime'da kompilyatsiya qiladi. Agar animatsiya paytida yangi, ilgari ko'rilmagan effekt (masalan, maxsus soya) birinchi marta paydo bo'lsa, shu kompilyatsiya narxi o'sha kadrda "to'lanadi" — natijada foydalanuvchi qisqa sakrashni ko'radi. Impeller ko'p shader'larni build vaqtida oldindan tayyorlab qo'yadi, shu sinfdagi jank'ni bartaraf etadi.

Savol: Isolate'lar orasida ma'lumot "nusxa ko'chirish" orqali uzatilishining amaliy narxi nima?

Javob: Katta ma'lumot strukturasini (masalan, bir necha o'n minglab elementli ro'yxat) isolate'lar orasida yuborish — butun strukturani nusxalashni talab qiladi, bu vaqt va xotira sarflaydi. Shu sababdan isolate'lar orasida juda katta hajmdagi ma'lumotni tez-tez almashtirish (masalan, har kadrda) samarasiz bo'ladi — isolate'lar ko'proq "bir marta katta ishni bajar, kichik natijani qaytar" stsenariysi uchun mos. TransferableTypedData kabi maxsus mexanizmlar ba'zi holatlarda nusxalash narxini kamaytirish uchun mavjud.

Savol: Agar build() metodi ichida uzoq davom etadigan sinxron hisoblash bo'lsa, nima bo'ladi?

Javob: U to'g'ridan-to'g'ri UI thread'ni (root isolate'ni) bloklaydi — chunki build() sinxron kod, event loop uni bo'lib yubora olmaydi. Natijada butun ilova (gesture'lar, animatsiyalar, scroll) o'sha hisoblash tugagunga qadar "muzlab qoladi". To'g'ri yechim — og'ir hisoblashni build()dan tashqariga, masalan initStateda oldindan hisoblab qo'yish, yoki CPU-og'ir bo'lsa compute() orqali alohida isolate'ga chiqarish.

Savol: Hot reload nega ba'zan "State'ni saqlab qololmadim, hot restart qiling" degan xabar beradi?

Javob: Hot reload yangi kodni ishlayotgan isolate'ning mavjud xotira tuzilishiga moslashtirishga harakat qiladi. Agar o'zgarish shu tuzilishni tubdan buzsa — masalan, sinf ierarxiyasi o'zgargan, enumga yangi qiymat qo'shilgan, generic parametr soni o'zgargan — VM eski obyektlarni yangi sxemaga "moslay olmaydi" va xavfsiz tarzda davom eta olmasligini bildiradi. Bunday holda faqat isolate'ni to'liq qayta yaratish (hot restart) xavfsiz.

Savol: Flutter Engine'ning C++ qatlamida yozilgan bo'lishi dasturchi uchun nima anglatadi?

Javob: Amaliy dasturchi (Framework darajasida ishlaydigan) uchun bu odatda ko'rinmas — Dart kodi Engine'ning C++ API'lari orqali Skia/Impeller'ga, matn tizimiga va platform kanallariga murojaat qiladi, ammo bu chaqiruvlar Framework qatlamida allaqachon Dart wrapper'lar bilan o'ralgan. Bu bilim asosan performance debugging'da (masalan, DevTools'da "raster thread band" degan belgini ko'rganda, buning nima ekanligini tushunish uchun) yoki engine darajasida hissa qo'shmoqchi bo'lgan dasturchilar uchun muhim bo'ladi.

Savol: Nega "debug rejimda sekin, release rejimda tez" degan tushuncha to'liq to'g'ri emas — qaysi jihatdan aniqroq bo'lish kerak?

Javob: Farq faqat "sekin/tez" emas — debug rejimda Dart VM qo'shimcha xavfsizlik tekshiruvlari (assertion'lar, tip tekshiruvlari) bilan ishlaydi va JIT orqali runtime'da kompilyatsiya qiladi, bu ma'lum darajada narx qo'shadi. Lekin real performance profiling hech qachon debug yoki hatto oddiy release build'da emas, balki maxsus --profile rejimida qilinishi kerak — chunki release rejimda ba'zi profiling signallari o'chirilgan bo'ladi, debug rejim esa sun'iy ravishda sekin.

Amaliy topshiriq

Microtask va event navbati tartibini bashorat qiling

To'rtta xil manbadan (sinxron kod, Future.microtask, Future(...), va Future.delayed(Duration(milliseconds: 100))) print chiqaradigan kichik Dart dasturi yozing. Chiqish tartibini oldindan yozib qo'ying, so'ng dasturni ishga tushirib, bashoratingiz to'g'ri chiqqan-chiqmaganini tekshiring va nega shunday tartib paydo bo'lganini o'z so'zlaringiz bilan tushuntiring.

Maslahatni ko'rsatish

Avval qog'ozda tartibni yozib chiqing (qaysi print qachon), keyin kodni haqiqatan ishga tushirib tekshiring. Future(), Future.delayed(), Future.microtask() va sinxron print()larni turli tartibda aralashtirib ko'ring.