أدوات التحليل الأدائي
أدوات التحليل الأدائي
يخبرك القياس المعياري بمدى سرعة كودك. أما التحليل الأدائي (Profiling) فيخبرك بسبب ذلك. يتّصل المحلّل الأدائي بـ JVM قيد التشغيل ويأخذ عيّنات مستمرّة أو يعتمد التوثيق الآلي (instrumentation)، ليمنحك تفصيلًا حيًّا لوقت المعالج وتخصيصات الكومة (heap) وحالات الخيوط وجمع القمامة والتزاحم على الأقفال — وهو بالضبط ما تحتاجه لإصلاح مشكلة قستَها بالفعل.
في هذا الدرس نتناول الأدوات التحليلية الثلاث الأساسية للعمل الاحترافي في Java: Java Flight Recorder (JFR) وVisualVM وفنّ قراءة تفريغ الكومة (heap dump).
Java Flight Recorder
JFR محلّل أدائي آمن للإنتاج ومنخفض الحِمل، مدمج في JVM ذاته. جرى فتح مصدره كجزء من OpenJDK في Java 11 ويأتي مع كل JDK منذ ذلك الحين — لا تنزيل إضافي ولا عامل (agent) ولا رسوم ترخيص. يبلغ حِمله عادةً أقل من 1% في معظم أعباء العمل، ما يجعله آمنًا للتشغيل المستمر في الإنتاج.
يعمل JFR عبر تسجيل أحداث. كل نظام فرعي في JVM (GC وJIT وتحميل الأصناف و I/O للمقبس والأقفال و I/O للملفات وغيرها) يُطلق أحداثًا بطوابع زمنية وبيانات وصفية. يخزّن JFR تلك الأحداث في تنسيق ثنائي عالي الكفاءة في ذاكرة دورية (ring buffer) ويصرفها إلى ملف .jfr عند الطلب. تفتح ذلك الملف لاحقًا في JDK Mission Control (JMC) لتحليله.
بدء التسجيل
ثمّة ثلاث طرق لبدء تسجيل JFR.
1. عند تشغيل JVM (الأفضل للإمساك بالمشكلات منذ البداية):
2. عند الطلب عبر jcmd أثناء تشغيل التطبيق (دون إعادة تشغيل):
3. برمجيًا من داخل التطبيق:
default ليكون حِمله قريبًا من الصفر؛ يسجّل أحداث GC وبعض أحداث I/O. يضيف إعداد profile أخذ عيّنات المعالج كل 10 مللي ثانية وتحليل تخصيص الكائنات وتحليل الأقفال — تفاصيل أكثر، حِمل لا يزال منخفضًا جدًا لكن ليس صفرًا. استخدم default في الإنتاج باستمرار، وانتقل إلى profile للتحقيقات المستهدفة.
قراءة ملف JFR في JDK Mission Control
حمّل JMC من jdk.java.net/jmc (تنزيل منفصل عن JDK). افتح ملف .jfr واستكشف:
- التحليل التلقائي — يفحص JMC التسجيل ويُبرز الشذوذات (توقّف GC طويل، نقطة تزاحم أقفال، تخصيصات مشبوهة). ابدأ من هنا.
- تحليل الدوالّ — رسم لهب (flame graph) أو شجرة استدعاء لوقت المعالج المُعاين. الإطار الأشد سخونةً هو اختناقك الأدائي.
- الذاكرة — تحليل التخصيص يُظهر مواقع الاستدعاء الأكثر تخصيصًا، وحجم الكومة الحيّة عبر الزمن.
- الخيوط — جدول زمني لحالة الخيوط: أخضر = تنفيذ، أصفر = انتظار قفل، بنفسجي = نوم. الشرائط الصفراء العريضة تعني تزاحمًا.
- جمع القمامة — كل توقّف لـ GC ونوعه ومدّته والكومة قبله وبعده والسبب الذي أطلقه.
maxsize=250m بلا duration) وتفريغها عند الطلب حين تقع حادثة — ستحصل على بيانات التحليل الأخيرة لدقائق قبل المشكلة مباشرةً، دون أن تكون خططت مسبقًا. هذا أفيد بكثير من إرفاق محلّل أدائي بعد وقوع الحادثة.
VisualVM
VisualVM محلّل أدائي مجاني قائم على واجهة رسومية يتّصل بـ JVM محلّي أو بعيد عبر JMX. يُثبَّت بشكل منفصل عن JDK (من visualvm.github.io) وهو أيسر نقطة بداية للمطوّرين الراغبين في عرض بصري وآني لعملية جارية.
ما يمنحك إيّاه VisualVM بلمحة:
- النظرة العامة — أعلام JVM وخصائص النظام ومدة التشغيل وPID.
- المراقبة — نسبة استخدام المعالج والكومة المستخدمة/المحجوزة وعدد الخيوط والأصناف لحظيًا. يُظهر على الفور ما إذا كانت الكومة تنمو أو يوجد ارتفاع مفاجئ في المعالج.
- الخيوط — جدول خيوط حيّ، وتفريغ الخيوط عند الطلب. تُكتشف الإغلاقات المتبادلة (deadlocks) وتُبرز تلقائيًا.
- أخذ العيّنات — أخذ عيّنات المعالج والذاكرة بحِمل منخفض. يُظهر أخذ عيّنات المعالج جدول الدوالّ الساخنة؛ وأخذ عيّنات الذاكرة يُظهر التخصيص حسب الصنف. استخدم هذا التبويب للتحقيقات السريعة دون إيقاف العالم.
- التحليل الدقيق — وضع التوثيق الآلي (يُحصي كل دخول/خروج للدالّة). أكثر دقةً لكنه يُضيف حِملًا — تجنّبه على حركة الإنتاج.
- تفريغ الكومة — أطلق تفريغًا أو استورد واحدًا واستعرض أعداد الكائنات والأحجام المحتجزة وسلاسل المراجع.
للاتّصال بعملية بعيدة، شغّل JVM بتفعيل JMX:
في VisualVM اختر ملف ← إضافة اتصال JMX وأدخل host:9010.
ssh -L 9010:localhost:9010 user@host، ثم اتّصل بـ localhost:9010 في VisualVM.
قراءة تفريغ الكومة
تفريغ الكومة هو لقطة لجميع الكائنات الحيّة في كومة JVM في لحظة واحدة. إنّه الأداة القاطعة لتشخيص تسرّبات الذاكرة: كل شيء على الكومة مرئي، إضافةً إلى سلاسل المراجع التي تُفسّر لماذا الكائنات لا تزال حيّة.
التقاط تفريغ الكومة
-XX:+HeapDumpOnOutOfMemoryError في الإنتاج. إذا تعطّل التطبيق بسبب نفاد الذاكرة ستحصل على التفريغ تلقائيًا، وهذا في الغالب الفرصة الوحيدة لمعرفة ما كان يستهلك الكومة في تلك اللحظة بالذات.
تحليل التفريغ
افتح ملف .hprof في Eclipse Memory Analyzer Tool (MAT) أو عارض تفريغ الكومة في VisualVM. المفاهيم الأساسية التي يجب فهمها:
- الحجم الضحل (Shallow size) — الذاكرة التي يستخدمها الكائن نفسه (حقوله). مفيد لفهم تخطيط الكائن.
- الحجم المحتجز (Retained size) — الذاكرة التي ستُحرَّر لو جُمع هذا الكائن كقمامة، أي الكائن بالإضافة إلى كل ما يمكن الوصول إليه حصريًا عبره. هذا ما يهمّ في تحليل التسرّب.
- شجرة المهيمنين (Dominator tree) — شجرة تمثّل لكل عقدة الكائنَ الذي ستُحرَّر أكبر كمية ذاكرة بحذفه. قمّة شجرة المهيمنين تُظهر أكبر مستهلكي الذاكرة؛ هذا المكان الذي تنشأ منه التسرّبات عادةً.
- جذور GC (GC roots) — نقاط البداية التي يتتبّع منها GC قابلية الوصول: الحقول الساكنة، ومكدّسات الخيوط، ومراجع JNI. الكائن حيّ لأن سلسلة مراجع تقوده إليه من جذر GC. التسرّب هو الرابط الذي كان يجب أن يُحذف.
في MAT شغّل تقرير Leak Suspects أوّلًا. يكتشف تلقائيًا كائنات التجميع (مجموعات أو ذاكرات تخزين مؤقّت تحتفظ بعشرات الآلاف من الإدخالات) ويتتبّعها وصولًا إلى جذر GC. ثم تنظر في سلسلة المراجع لتفهم أيّ مكوّن يحتفظ بالمرجع ولماذا لم يُحرَّر أبدًا.
.hprof بحجم 4 جيجابايت (غير مضغوط). يحتاج MAT إلى ما يقارب 1–1.5 ضعف ذلك كذاكرة Java لتحليله. استخدم MAT بنسخة 64-بت وامنحه على الأقل -Xmx6g عند تحليل تفريغات كبيرة. عارض تفريغ الكومة المدمج في VisualVM يعمل بشكل أفضل مع تفريغات أصغر (أقل من 1 جيجابايت).
اختيار الأداة المناسبة
- JFR + JMC — التحليل الأدائي المستمر في الإنتاج، توقيت دقيق، حِمل ضئيل، نظام أحداث ثريّ. الخيار الأوّل للتحقيقات الإنتاجية.
- VisualVM — سريع، بصري، رائع للتطوير المحلي والفحوصات السريعة على التجهيز. سهل لمشاركة النتائج عبر لقطات الشاشة.
- تفريغ الكومة + MAT/VisualVM — تشخيص تسرّبات الذاكرة وفهم أشجار الكائنات الحيّة. ليست أداة آنية؛ تُستخدم بشكل رجعي.
تجمع سير عمل الأداء الناضج بين الثلاثة: شغّل JFR باستمرار، واستخدم VisualVM للاستكشاف الحيّ أثناء التطوير، واجعل التقاط تفريغ الكومة تلقائيًا عند نفاد الذاكرة.