Bytecode — source code bilan fizik protsessor machine code’i orasidagi oraliq instruction formati. U odatda virtual mashina, interpreter yoki JIT compiler tomonidan bajariladi.
Bytecode ma’lum CPU modeliga emas, runtime belgilagan virtual instruction setga mo‘ljallanadi. Shu sababli bir xil bytecode turli operatsion tizim va protsessorlarda tegishli runtime mavjud bo‘lsa ishlashi mumkin.
Yaratilish jarayoni
Oddiy pipeline:
Source code
→ Compiler
→ Bytecode
→ Virtual machine
→ Machine instruction
Bytecode compile vaqtida yaratiladi. Runtime uni:
- interpretatsiya qiladi;
- JIT orqali native kodga aylantiradi;
- AOT orqali oldindan compile qiladi;
- aralash usulda bajaradi.
Virtual instruction set
Bytecode o‘z instructionlariga ega.
Misollar:
- constant yuklash;
- local variable olish;
- arithmetic;
- method chaqirish;
- object yaratish;
- branch;
- return;
- exception;
- type check.
Instructionlar ko‘pincha bir yoki bir nechta baytdan iborat bo‘ladi. “Bytecode” nomi shundan kelgan, ammo har instruction aynan bir bayt bo‘lishi shart emas.
Stack-based bytecode
Ko‘p virtual mashina operand stackdan foydalanadi.
Misol:
push 2
push 3
add
return
add stackdagi ikki qiymatni oladi va natijani qayta stackka qo‘yadi.
Stack-based format ixcham va compiler uchun sodda.
Register-based bytecode
Boshqa virtual mashina virtual registerlardan foydalanadi.
Misol:
load r1, 2
load r2, 3
add r3, r1, r2
return r3
Instruction uzunroq bo‘lishi mumkin, ammo kamroq instruction talab qilishi ehtimoli bor.
Platform mustaqilligi
Machine code x86-64, ARM64 yoki boshqa architecture uchun alohida yaratiladi.
Bytecode esa runtime specificationga bog‘liq.
Masalan, bir bytecode fayl:
- Linux x86-64;
- Windows x86-64;
- ARM64
runtime’larida ishlashi mumkin.
Platformaga xos native library yoki system API ishlatilsa, to‘liq mustaqillik cheklanadi.
Metadata
Bytecode fayli faqat instructionlardan iborat bo‘lmasligi mumkin.
Unda:
- class;
- method;
- type;
- constant pool;
- symbol;
- annotation;
- debug line;
- dependency;
- exception table;
- version
saqlanadi.
Runtime verification va reflection shu metadata’dan foydalanadi.
Constant pool
String, class nomi, method reference va literal qiymatlar umumiy jadvalda saqlanishi mumkin.
Instruction constant pool indeksiga murojaat qiladi.
Bu formatni ixchamlashtiradi va linkingni runtime’ga qoldiradi.
Verification
Managed runtime bytecode’ni bajarishdan oldin tekshirishi mumkin.
Tekshiruvlar:
- instruction chegarasi;
- stack balansi;
- type mosligi;
- branch target;
- local variable;
- access permission;
- invalid opcode.
Bu xotira xavfsizligi va runtime barqarorligini oshiradi.
Verification zararli kodni to‘liq xavfsiz qilmaydi, ammo noto‘g‘ri formatni rad etadi.
Interpreter
Interpreter bytecode instructionlarini ketma-ket o‘qib bajaradi.
Afzalliklari:
- tez startup;
- sodda implementatsiya;
- kam qo‘shimcha memory;
- profiling ma’lumoti yig‘ish.
Kamchiligi — har instruction uchun decode va dispatch overhead.
JIT compilation
JIT tez-tez ishlatiladigan bytecode qismlarini native machine code’ga aylantiradi.
Runtime haqiqiy type va branch profilidan foydalanib chuqur optimization bajarishi mumkin.
Sovuq kod interpreterda qolishi, issiq kod JIT qilinishi mumkin.
AOT compilation
Bytecode install yoki build vaqtida native code’ga aylantirilishi mumkin.
Bu startupni tezlashtiradi va JIT talabini kamaytiradi.
Runtime metadata, garbage collection va dynamic linking baribir saqlanishi mumkin.
Bytecode cache
Interpreted til source code’ni parse qilish xarajatini kamaytirish uchun bytecode cache yaratishi mumkin.
- source hash;
- timestamp;
- interpreter versiyasi;
- optimization flag
bilan bog‘liq.
Eski runtime bytecode cache’ni o‘qimasligi va source’dan qayta yaratishi mumkin.
Version
Bytecode format runtime versiyasiga bog‘liq.
Yangi compiler:
- yangi opcode;
- yangi metadata;
- boshqa constant format
yaratishi mumkin.
Eski runtime bunday faylni rad etadi.
Backward yoki forward compatibility specification bilan belgilanadi.
Obfuscation
Bytecode machine code’dan yuqori darajali metadata saqlashi mumkin. Shu sababli decompile qilish osonroq bo‘lishi mumkin.
Obfuscator:
- symbol nomlarini o‘zgartiradi;
- control flow’ni murakkablashtiradi;
- metadata’ni kamaytiradi;
- stringni yashiradi.
Obfuscation maxfiy algoritmni mutlaq himoya qilmaydi.
Decompilation
Bytecode’dan source’ga o‘xshash kod tiklanishi mumkin.
Ammo:
yo‘qolgan bo‘lishi mumkin.
Decompiled kod asl source bilan aynan bir xil emas.
Sandbox
Virtual machine bytecode instructionlari va APIlarini nazorat qilishi mumkin.
kirishini cheklaydi.
Faqat bytecode formatining o‘zi sandbox bermaydi; runtime policy zarur.
Debugging
Bytecode fayl source line mapping saqlasa, debugger source qatorini ko‘rsatadi.
Breakpoint runtime instruction yoki method bilan bog‘lanadi.
Optimization sabab bajarilish tartibi source’dan farq qilishi mumkin.
Performance tahlili
Bytecode darajasida:
- instruction soni;
- allocation;
- method dispatch;
- boxing;
- branch;
- virtual call
tahlil qilinadi.
Biroq real performance JIT hosil qilgan machine code va runtime profilingga ham bog‘liq.
Diagnostika
Bytecode muammosida:
- runtime versiyasi;
- format version;
- verifier xatosi;
- class yoki module path;
- dependency;
- corrupted file;
- cache;
- JIT log;
- source map yoki debug metadata
tekshiriladi.
“Unsupported class version” kabi xato compiler va runtime versiyasi mos emasligini bildiradi.
Bytecode va umumiy IR
Har intermediate representation bytecode emas. Compiler ichki SSA yoki graph ko‘rinishidagi IR’dan optimization uchun foydalanishi mumkin, ammo u tarqatish formati bo‘lmasligi mumkin. Bytecode odatda saqlash, yuklash va virtual mashinada bajarish uchun barqarorroq formatga ega.
Bog‘liq tushunchalar
Virtual machine, Interpreter, JIT, AOT, Compiler, Machine code, Runtime, Opcode, Verification, Decompiler