JIT (Just-In-Time) — فناوری کامپایل پویایی که کد بایت یا نمایش واسطی برنامه را به دستورات ماشین مستقیماً در زمان اجرا تبدیل میکند. در Android کامپایلکننده JIT برای نخستین بار در نسخه 2.2 Froyo در ماشین مجازی Dalvik ظاهر شد و اجرای برنامهها را 2–5 برابر سریعتر کرد. به گزارش Google, 2024، JIT مدرن در ART تفسیر را با کامپایل نمونهگیری شده روشهای hot ترکیب میکند.
نکات کلیدی
Just-In-Time (JIT) — روش کامپایلی که در آن کد منبع یا کد بایت نه از پیش (مانند AOT)، بلکه در لحظه فراخوانی اول قسمت مربوطه برنامه به دستورات ماشین تبدیل میشود. اصطلاح «Just-In-Time» به این معناست که کامپایل دقیقاً بهموقع — درست قبل اجرا انجام میشود.
مفهوم JIT از دهه 1960 میلادی وجود دارد، اما با ظهور Java Virtual Machine در سال 1995 به طور گسترده مورد استفاده قرار گرفت. JIT امکان ترکیب قابلیت انتقال کد بایت (یکبار مینویسیم — هرجا اجرا میکنیم) را با کارایی نزدیک به کد طبیعی فراهم میکند. در Java HotSpot VM، کامپایلر JIT کد در حال اجرا را تجزیه و تحلیل کرده و برای صرفهجویی در زمان و حافظه، تنها بازنتیجههای بیشترین حیاس را کامپایل میکند.
کامپایلر JIT کد بایت را دریافت میکند، آن را تفسیر میکند و به موازات آمار جمعآوری میکند. هنگامی که قسمتی از کد (روش، حلقه) به میزان کافی تکرار فراخوانی شود، JIT تصمیم به کامپایل میگیرد. کد ماشین کامپایل شده در کش ذخیره میشود — در فراخوانیهای بعدی از نسخه آماده استفاده میشود. این شتاب را بدون نیاز به کامپایل کل برنامه فراهم میکند.
// مثال: روش پس از فراخوانی مکرر hot میشود
public class HotMethod {
private int compute(int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += i * i;
}
return sum;
}
}
// فراخوانی 500 بار در حلقه — JIT compute را کامپایل میکند
for (int t = 0; t < 500; t++) {
hot.compute(1000);
}
در Android کامپایل JIT سه مرحله تکامل را طی کرده است. مرحله اول — Dalvik بدون JIT (Android 1.0–2.1): تفسیر خالص کد بایت DEX. مرحله دوم — Dalvik با JIT (Android 2.2–4.4): ظهور کامپایلر JIT که برنامهها را 2–5 برابر سریعتر کرد. مرحله سوم — ART با JIT ترکیبی (Android 7.0+): بازگشت JIT در کیفیتی جدید.
JIT در Dalvik به عنوان یک کامپایلر trace-based پیادهسازی شده بود. آن روشهای فردی را تحلیل نمیکرد، بلکه زنجیره دستوراتی (traces) را که به صورت متوالی اجرا میشوند. این امکان کامپایل مسیرهای اجرایی کامل را که چندین روش را شامل میشد، فراهم میکرد. چنین رویکردی برای کل ماشینهای فاردو موبایل با کش کوچک دستورات موثر بود، زیرا trace کامپایل شده در کش L1 قرار میگرفت.
از Android 7.0 Nougat به بعد، ART از method-based JIT استفاده میکند — روشهای فردی را بر اساس نیروگیری های اجرا کامپایل میکند. این JIT به مراتب سریعتر از Dalvik JIT کار میکند: زمان تیپیک کامپایل یک روش 0.5–1 ms در مقابل 3–5 ms در Dalvik است. کد کامپایل شده در یک منطقه حافظه جداگانه (JIT code cache) ذخیره میشود، نه در کاپه برنامه، که فرگمانتاسیون را کاهش میدهد.
| پارامتر | Dalvik JIT | ART JIT |
|---|---|---|
| نوع | Trace-based | Method-based |
| سرعت کامپایل | 3–5 ms/روش | 0.5–1 ms/روش |
| آستانه کامپایل | ~200 بار فراخوانی | پویا |
| کش کد | در کاپه برنامه | JIT code cache |
| نیروگیری | داخلی | فایلهای .prof خارجی |
مکانیسم مرکزی JIT — تشخیص روشهای hot. هر فراخوانی روش یک شمارنده داخلی را افزایش میدهد. وقتی شمارنده از آستانه گذشت، روش به عنوان «داغ» علامتگذاری شده و برای کامپایل ارسال میشود. در Dalvik آستانه به صورت سخت تعیین شده بود (~200 فراخوانی). در ART شمارندهها به صورت پویا بسته به منابع موجود دستگاه تنظیم میشوند.
فرآیند کامپایل چندین مرحله را شامل است. مرحله اول — تجزیه و تحلیل کد بایت: JIT جریان دستورات را بررسی کرده و گراف جریان داده (data-flow graph) را میسازد. مرحله دوم — بهینهسازی: inline کردن روشهای کوچک، حذف کد مرده، تاکردن ثابتها. مرحله سوم — تولید کد: تبدیل گراف بهینه شده به دستورات ماشین برای معماری مشخص CPU (ARM، ARM64، x86).
// نمایش inlining — JIT بدنه روش را جایگزین میکند
public int inlineExample() {
return square(5);
}
private int square(int x) {
return x * x;
} // JIT فراخوانی را با return 5 * 5; جایگزین میکند
یک تکنیک ویژهٔ JIT — On-Stack Replacement (OSR). اگر روش شامل یک حلقه طولانی است که صدها تکرار طول میکشد، JIT میتواند حلقه را در حال پرواز کامپایل کرده و نسخه تفسیری را با نسخه کامپایل شده در همان حال اجرا جایگزین کند. OSR به ویژه برای وظایف محاسباتی موثر است: رندرینگ، پردازش تصویر، ارتقام.
JIT و AOT — دو رویکرد به کامپایل با توافقهای متضاد. JIT سرعت اجرای اول را به خاطر چگالی توزیع و سازگاری قربان میکند. AOT زمان نصب و حجم دیسک را به خاطر حداکثر کارایی از ثانیه اول قربان میکند. هیچکدام از رویکردها به طور مطلق بهتر نیستند — انتخاب به سناریو بستگی دارد.
مزیت کلیدی JIT بهینهسازی سازگار است. JIT میتواند از اطلاعات نیروگیری استفاده کند که برای AOT در دسترس نیست: انواع دقیق اشیا، تکرار واقعی فراخوانیها، شاخههای واقعی. این امکان اعمال بهینهسازیهای پرتاجی را که در کامپایل استاتیک ممکن نیست، فراهم میکند. به عنوان مثال، JIT میتواند فراخوانی روشها را مجازی کند (devirtualize)، اگر در عمل تنها یک نوع دریافتکننده وجود داشته باشد.
| معیار | JIT | AOT |
|---|---|---|
| زمان نصب | فوری | بستگی به اندازه |
| اجرای اول | کندتر (گرمشدن) | سریع |
| حجم دیسک | حداقل | +15–30% |
| سازگاری | بالا | پایین |
| مصرف CPU | اوج در حین کامپایل | پایدار |
کامپایل JIT زمانی ترجیح داده میشود که سرعت استقرار و صرفهجویی در فضای دیسک اهمیت داشته باشد. در بزرگراه توسعه موبایل، JIT برای برنامههایی که مکرراً بهروزرسانی میشوند (آزمایش A/B، hotfix) ایدئال است. JIT همچنین در مرحله توسعه راحت است، وقتی کد دههها بار در روز بازسازی میشود — هر ثانیه صرفه جویی در کامپایل چرخه بازخورد را سریعتر میکند.
JIT یک سری مزایای عملی را در اختیار توسعهدهندگان قرار میدهد. اولین — حجم کوچک APK. در رویکرد JIT، تنها کد بایت (DEX) در APK بستهبندی میشود که 20–30% کمتر از کد ناتیو کامپایل شده فضا اشغال میکند. برای کاربرانی که حافظه داخلی محدودی دارند، این یک مزیت قابل توجه است.
مزیت دوم — سازگاری با دستگاه. JIT کد را با توجه به معماری واقعی CPU، میزان RAM و بار فعلی کامپایل میکند. به عنوان مثال، در دستگاهی با 2 گیگابایت RAM، JIT میتواند کمتر پرتاج کامپایل کرده و در حافظه صرفهجویی کند، و در یک پرچمدار 12 گیگابایتی تمام بهینهسازیهای ممکن را اعمال کند. کامپایل AOT، در مقابل، تصمیم را در لحظه نصب ثابت میکند.
کد بایت مستقل از پلتفرم باقی میماند که توزیع برنامهها را ساده میکند. یک APK بر روی دستگاههای ARM، ARM64 و x86 کار میکند و JIT تولید کد ناتیو را برای هر معماری فراهم میکند. برای رویکرد AOT، یا باید چندین واریانت کد ناتیو را در APK قرار داد (افزایش اندازه) یا برای هر معماری یک نسخه جداگانه کامپایل کرد.
نقص اصلی JIT تاخیر گرمشدن (warm-up delay) است. کاربر در ثانیه های اول کار برنامه کندی را میبیند در حالی که JIT روشهای hot را کامپایل میکند. در بازیها این به صورت «تقلگ» (stuttering) در مراحل اولیه ظاهر میشود. در برنامههای با انیمیشن — لرزش در انتقالهای اول بین صفحات.
نقص دوم مصرف انرژی است. فرآیند کامپایل به شدت CPU را مشغول کرده و مصرف انرژی را در دوره گرمشدن تا 10–20% افزایش میدهد. در دستگاههای باتریدار این زمان کار با باتری را کاهش میدهد. به ویژه در سناریوهایی با راهاندازی مکرر برنامهها (چند وظیفهای با حافظه محدود که سیستم فرآیندها را تخلیه و مجدداً بارگیری میکند).
مشکل دیگر تکه تکه شدن کش JIT است. کد کامپایل شده در یک منطقه حافظه همرو ذخیره میشود. با بارگیری کلاسهای جدید و کامپایل روشهای اضافی، کش تکه تکه شده و هزینه های مدیریت حافظه افزایش مییابد. در Dalvik این مشکل با پاکسازی دورهای کش حل میشد؛ در ART کش JIT جداگانه از کاپه تخصیص داده میشود و از راهبرد خود برای دفراگمانتاسیون استفاده میکند.
رویکرد مدرن در ART — کامپایل ترکیبی، قوتهای JIT و AOT را ترکیب میکند. در حین نصب برنامه، کامپایل انجام نمیشود — تنها بررسی کد بایت (verify). این نصب سریع و اشغال فضای حداقل را تضمین میکند. اجراهای اولیه در حالت تفسیر با کامپایل JIT روشهای hot کار میکنند — کاربر بدون انتظار طولانی عملکرد قابل قبولی دریافت میکند.
به موازات، نیروگیر پسزمینه دادههایی درباره استفاده واقعی جمعآوری میکند. پس از 2–3 اجرای کامل برنامه، نیروگیر به کاملیت کافی میرسد و سیستم dex2oat را برای کامپایل روشهای hot به کد ناتیو آغاز میکند. این عملیات در پسزمینه و زمانی که دستگاه مشغول نیست (در حال شارژ، صفحه نمایش خاموش) انجام میشود. پس از اتمام AOT پسزمینه، برنامه عملکردی مشابه با کامپایل کامل AOT ارائه میدهد.
# اجرای اجباری کامپایل پسزمینه
adb shell cmd package compile -m speed-profile -f com.example.app
# نمایش وضعیت کامپایل
adb shell cmd package dump-profiles com.example.app
به گزارش Google I/O 2017، کامپایل ترکیبی زمان نصب برنامهها را نسبت به AOT خالص 30–50% کاهش داده است. حجم اشغال شده در بخش سیستم 20–30% کاهش یافته است. عملکرد پس از کامپایل پسزمینه با سطح AOT کامل برابر است. تنها سناریویی که حالت ترکیبی از AOT عقب است، اجرای اول بلافاصله پس از نصب است: برنامه در حالت JIT کار کرده و میتواند 10–15% کندتر باشد.
سوالات متداول
JIT — روشی برای سریعتر کردن برنامه است که در آن کد به زبان ماشین نه از پیش، بلکه به صورت قسمتقسمت در زمان کار ترجمه میشود. پرکاردارترین قسمتها کامپایل و ذخیره میشوند و قسمتهای نادر در شکل اصلی خود باقی میمانند.
JIT کد را در زمان اجرا کامپایل میکند که به صرفهجویی در فضا و سرعت نصب کمک میکند. AOT تمام کد را از پیش کامپایل میکند — برنامه سریعتر اجرا میشود اما به فضای بیشتر و زمان نصب بیشتری نیاز دارد.
JIT حذف نشده، بلکه تکامل یافته. در Android 5.0، Dalvik با JIT با ART و AOT خالص جایگزین شد. در Android 7.0، JIT به ART بازگشت به عنوان بخشی از یک سیستم ترکیبی که برای عملکرد بهینه با AOT پسزمینه همکاری میکند.
JIT مصرف انرژی را در دوره گرمشدن به دلیل بار CPU تا 10–20% افزایش میدهد. پس از اتمام کامپایل روشهای hot، مصرف انرژی به سطح عادی بازمیگردد. حالت ترکیبی ART این پیکها را به لطف کامپایل پسزمینه به حداقل میرساند.
بله، در سناریوهایی با محاسبات پیچیده. کاربر میتواند در ثانیه های اول کار برنامه یا اوایل بازی کندی را مشاهده کند. در نسخههای مدرن Android (8.0+)، حالت ترکیبی این اتفاق را به لطف کامپایل نمونهگیری شده به حداقل میرساند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید