اكتشاف تسرّبات الذاكرة وإصلاحها
حدّد الأنماط الشائعة لتسرّب الذاكرة، مثل المجموعات الثابتة والمستمعين وذاكرات التخزين المؤقت، وأصلحها
اكتشاف تسرّبات الذاكرة وإصلاحها درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
ما تسرّب الذاكرة في Java؟
يحدث تسرّب الذاكرة في Java عندما لا تعود الكائنات مطلوبة، لكنها تظل قابلة للوصول بقوة، مما يمنع GC من استعادة ذاكرتها. وتنمو الكومة حتى يُطرح OutOfMemoryError.
تراكم المجموعة الثابتة
يُعدّ الحقل static الذي يحتفظ بمجموعة متنامية مثالًا تقليديًا على التسرّب. وتظل الكائنات التي أُضيفت ولم تُزل حية طوال مدة تشغيل التطبيق.
public class Cache {
// LEAK: static list grows forever if items are never removed
private static final List<Object> items = new ArrayList<>();
public static void add(Object o) { items.add(o); }
// Fix: add a remove() method or use a bounded cache
}مستمعو الأحداث غير المُلغى تسجيلهم
يؤدي تسجيل مستمع دون إلغاء تسجيله لاحقًا إلى إبقاء المستمع وجميع الكائنات التي يشير إليها حيّة. ألغوا تسجيل المستمعين دائمًا عند عدم الحاجة إليهم.
// Leak:
sensor.addListener(new DataLogger());
// Fix:
DataLogger logger = new DataLogger();
sensor.addListener(logger);
// ... when done:
sensor.removeListener(logger);متغيرات ThreadLocal غير المنظّفة
في مسابح الخيوط، تبقى قيم ThreadLocal بين المهام لأن الخيوط يعاد استخدامها. وإذا لم تُمسح هذه القيم، تتسرب بيانات إحدى المهام إلى المهام اللاحقة على الخيط نفسه.
private static final ThreadLocal<MyContext> CTX = new ThreadLocal<>();
// Always clean up after each task:
try {
CTX.set(new MyContext(requestId));
doWork();
} finally {
CTX.remove(); // prevents leak in pooled threads
}تسرّبات محمّل الفئات في التطبيقات ذات النشر الساخن
في حاويات servlet، يستخدم كل نشر محمّل فئات جديدًا. وإذا احتفظت أي فئة بمرجع static إلى فئة من محمّل الفئات القديم، فلن يمكن جمع محمّل الفئات القديم بأكمله، ولا أي من فئاته، بواسطة GC.
اكتشاف تسرّبات الذاكرة باستخدام تفريغات الكومة
استخدم jmap -dump:format=b,file=heap.hprof <pid> لإنشاء تفريغ للكومة، ثم افتحه في Eclipse MAT أو VisualVM للعثور على أكبر الكائنات المحتجَزة وتحديد جذور GC الخاصة بها.
// Take a heap dump:
jmap -dump:live,format=b,file=heap.hprof $(jps | grep MyApp | cut -d" " -f1)
// Or trigger on OOM:
// -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprofEclipse Memory Analyzer (MAT)
يحدّد تقرير "Leak Suspects" في MAT تلقائيًا الكائنات التي تشغل حجمًا كبيرًا من الكومة المحتجَزة، ويعرض المسار انطلاقًا من جذور GC. ابدأ بعرض "Dominator Tree" للعثور على أكبر مسببات المشكلة.
استخدام WeakReference لتجنّب التسرّبات
غلّف الكائنات المخزنة مؤقتًا أو كائنات المستمعين داخل WeakReference. يستطيع GC استردادها عند انخفاض الذاكرة. تحقّق دائمًا من أن نتيجة get() ليست فارغة.
Map<String, WeakReference<Image>> imageCache = new HashMap<>();
imageCache.put("logo", new WeakReference<>(loadImage("logo.png")));
Image logo = imageCache.get("logo") != null ? imageCache.get("logo").get() : null;
if (logo == null) logo = reload("logo.png"); // re-load if GCedذاكرات تخزين مؤقت محدودة باستخدام LinkedHashMap LRU
أعد تعريف removeEldestEntry في LinkedHashMap لوضع حد لحجم ذاكرة التخزين المؤقت وإخلاء الإدخال الأقل استخدامًا مؤخرًا تلقائيًا.
Map<String, String> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry<String, String> e) {
return size() > 100; // evict when over 100 entries
}
};تحليل التخصيصات باستخدام Java Flight Recorder
يسجّل JFR (Java Flight Recorder) ملفات تعريف التخصيصات بأقل قدر من الحمل. فعّله باستخدام -XX:StartFlightRecording وحلّل البيانات باستخدام JDK Mission Control.
// Start a 60-second JFR recording:
java -XX:StartFlightRecording=duration=60s,filename=rec.jfr MyApp
// Or via jcmd:
jcmd <pid> JFR.start duration=60s filename=rec.jfrقائمة التحقق من إصلاح التسرّبات
تحقّق من: المجموعات static، والمستمعات غير المسجّلة، وThreadLocals في التجمعات، وتسربات الاتصالات أو التدفقات (استخدم try-with-resources)، ومراجع الفئات الداخلية إلى الكائنات الخارجية، وذاكرات التخزين المؤقت التي لا تُخلي الإدخالات.
تحقّق سريعًا
ما خيار JVM الذي يفرّغ الكومة تلقائيًا عند طرح OutOfMemoryError؟
مراجعة
تشمل المصادر الشائعة لتسرّبات Java: المجموعات static، والمستمعات غير المسجّلة، وThreadLocals في التجمعات، وذاكرات التخزين المؤقت غير المحدودة. اكتشفها باستخدام تفريغات الكومة وMAT. أصلحها باستخدام WeakReferences، وبُنى محدودة الحجم، وإزالة المراجع دائمًا عند الانتهاء منها.
الأسئلة الشائعة
هل درس «اكتشاف تسرّبات الذاكرة وإصلاحها» مجاني؟
نعم — نص درس «اكتشاف تسرّبات الذاكرة وإصلاحها» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «اكتشاف تسرّبات الذاكرة وإصلاحها»؟
حدّد الأنماط الشائعة لتسرّب الذاكرة، مثل المجموعات الثابتة والمستمعين وذاكرات التخزين المؤقت، وأصلحها تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «اكتشاف تسرّبات الذاكرة وإصلاحها»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- مناطق ذاكرة JVM ودورة حياة الكائنات
- خوارزميات GC: Serial وG1 وZGC وShenandoah
- اكتشاف تسرّبات الذاكرة وإصلاحها
- أعلام ضبط GC وتحليل JVisualVM