مُحسِّن الترجمة الآنية (JIT)
مُحسِّن الترجمة الآنية (JIT)
تبدأ الـ JVM بتفسير البايت-كود — تنفيذ تعليمة واحدة في كل مرة دون تجميع البرنامج كاملًا. هذا النهج مرن وسريع الانطلاق، لكنه أبطأ بكثير من الكود المُجمَّع أصلًا في التطبيقات طويلة الأمد. يسدّ مُحسِّن الترجمة الآنية (JIT) هذه الفجوة: يراقب الدوال الأكثر تنفيذًا، ويُجمِّعها إلى كود ماكيني مُحسَّن أثناء التشغيل، ثم يستبدل مسار التفسير بالنسخة المُجمَّعة. فهم آلية JIT يُمكِّنك من كتابة كود يُحسِّنه JIT بقوة ويُساعدك في تشخيص الحالات النادرة التي يُفاجئك فيها سلوكه.
التفسير مقابل التجميع
عند تحميل الـ JVM لأي صف، تبدأ الدوال بايت-كودًا. يُعالج المُفسِّر كل بادئة بايت-كود واحدة تلو الأخرى. هذا رخيص الانطلاق (لا تأخير ترجمة، بصمة صغيرة) لكنه مُكلف على وحدة المعالجة للمسارات الساخنة لأن تكلفة التفسير تُدفع في كل استدعاء.
يراقب مُجمِّع JIT (في HotSpot: C1 وC2) عدادات استدعاء الدوال وتكرارات الحلقات. حين يتجاوز عداد حدًّا معينًا يُطلِق التجميع:
- C1 (مُجمِّع العميل) — تجميع سريع خفيف التحسين. يُستخدم أولًا (طبقات 1-3 في التجميع المُتدرِّج) ليستبدل المُفسِّر سريعًا بكود لا يزال يجمع بيانات تنصيف.
- C2 (مُجمِّع الخادم) — تجميع بطيء شديد التحسين. يعمل في الطبقة 4 للدوال الأشد سخونة. يُنتج كودًا يُنافس C++ المكتوب يدويًا.
النقاط الساخنة — كيف تكتشفها الـ JVM
تحتفظ الـ JVM بعدادين مستقلين لكل دالة:
- عداد الاستدعاء — يزداد في كل استدعاء للدالة.
- عداد الحافة الخلفية — يزداد في كل تكرار حلقة داخل الدالة (يُمكِّن الاستبدال أثناء التكديس).
حين يتجاوز أيٌّ من العدادين قيمة -XX:CompileThreshold (10,000 افتراضيًا في JVM الخادم)، تُضاف الدالة إلى طابور تجميع JIT. تتناقص العدادات مع مرور الوقت فقد تخرج الدالة الساخنة سابقًا من الكود المُجمَّع إذا توقف تنفيذها.
الاستبدال أثناء التكديس (OSR) هو آلية JIT التي تستبدل دالةً أثناء تنفيذها. هذا مهم للحلقات الطويلة: تستطيع الـ JVM تجميع الحلقة والتبديل إلى النسخة الماكينية منتصف التنفيذ دون انتظار الاستدعاء التالي.
التضمين — أهم تحسينات JIT
تضمين الدوال (Method Inlining) يستبدل موقع الاستدعاء بجسم الدالة المُستدعاة. يُلغي تكلفة الاستدعاء (إنشاء إطار المكدس، تمرير المُعطيات، الإرجاع)، لكن قوته الحقيقية أنه يكشف الكود المُضمَّن للسياق المحيط مما يُمكِّن تحسينات إضافية كطيّ الثوابت وحذف الكود الميت وفك لفّ الحلقات التي كانت مستحيلة عبر حدود الدوال.
يُضمِّن JIT استنادًا إلى حدود حجم البايت-كود وتكرار الاستدعاء. الحدود الرئيسية في HotSpot:
-XX:MaxInlineSize=35— تُضمَّن الدوال التي لا تتجاوز 35 بايت-كود شبه دائمًا إذا استُدعيت كثيرًا.-XX:FreqInlineSize=325— الدوال شديدة السخونة حتى 325 بايت-كود قد تُضمَّن أيضًا.-XX:MaxRecursiveInlineLevel— يتحكم في عمق التضمين التكراري.
إلغاء الافتراضية والتحسينات التخمينية
الإرسال الافتراضي (استدعاء دالة واجهة أو دالة مُجاوِبة) يمنع التضمين البسيط لأن JIT لا يعرف وقت الترجمة أيّ صف ملموس سيُستخدم. يستخدم JIT بيانات التنصيف المجمَّعة أثناء التفسير لاتخاذ افتراضات تخمينية:
- موقع استدعاء أحادي — نوع ملموس واحد فقط مُرصَد. يُضمِّن JIT تنفيذ ذلك النوع ويُضيف حارسًا. إذا أخفق الحارس (وصل نوع مختلف) يتراجع إلى المسار البطيء.
- موقع استدعاء ثنائي — نوعان ملموسان مُرصَدان. يُصدر JIT if/else على الجسمين المُضمَّنين.
- موقع استدعاء متعدد الأشكال — ثلاثة أنواع أو أكثر مُرصَدة. يتخلى JIT عن التضمين ويستخدم جدول الإرسال الافتراضي. هذا منحدر أداء حاد.
تحليل الهروب واستبدال القيم القياسية
يُجري JIT تحليل الهروب لتحديد ما إذا كان مرجع كائن قادرًا على الخروج من الدالة أو الخيط الحاليين. إذا لم يهرب الكائن، يستطيع JIT:
- تخصيصه على المكدس — تجنب التخصيص في الكومة وضغط جامع القمامة كليًّا.
- استبداله بالقيم القياسية — تفكيك الكائن إلى حقوله الأولية وتخزينها في سجلات المعالج، مما يُلغي تكلفة الكائن كليًّا.
مراقبة سلوك JIT
يمكنك مراقبة ما يفعله JIT دون أداة تنصيف:
الخلاصة العملية
- ابقِ الدوال صغيرة لتبقى ضمن حدود التضمين — التصميم الكيئي الجيد وصداقة JIT يتوافقان طبيعيًا.
- فضِّل مواقع الاستدعاء أحادية أو ثنائية في الحلقات الساخنة؛ تجنب تخزين أنواع ملموسة كثيرة مختلفة وراء نفس متغير الواجهة في حلقة ضيقة.
- دع الـ JVM يُسخِّن قبل قياس الأداء. استخدم JMH لأي معايرة جادة.
- ثق بـ JIT أولًا؛ الجأ إلى التحسينات الدقيقة اليدوية (مثل تجنب التغليف التلقائي) فقط بعد تأكيد أداة التنصيف لوجود عنق زجاجة حقيقي.
- استخدم
-XX:+PrintCompilationأثناء الاختبار للتحقق من أن المسارات الحرجة تُجمَّع في الطبقة 4.
في الدرس القادم سنتعلم كيفية قياس الأداء ومعايرته بصحة حتى لا تُنتج تأثيرات JIT من إحماء وحذف كود ميت نتائج مُضلِّلة.