لماذا يقوم برنامج Java الخاص بي بتسريب الذاكرة عندما أقوم باستدعاء run() على كائن Thread؟

StackOverflow https://stackoverflow.com/questions/107823

سؤال

(سؤال من نوع الخطر، أتمنى لو كانت الإجابة متاحة عبر الإنترنت عندما واجهت هذه المشكلة)

باستخدام Java 1.4، لدي طريقة أرغب في تشغيلها كسلسلة رسائل في بعض الأحيان، ولكن ليس في أوقات أخرى.لذلك أعلنتها كفئة فرعية من Thread، ثم قمت بتسميتها start() أو run() حسب ما أحتاجه.

لكنني وجدت أن برنامجي سوف يسرب الذاكرة بمرور الوقت.ما الخطأ الذي افعله؟

هل كانت مفيدة؟

المحلول

هذا خطأ معروف في Java 1.4:http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=5869e03fee226ffffffffc40d4fa881a86e3:WuuT?bug_id=4533087

لقد تم إصلاحه في Java 1.5 لكن Sun لا تنوي إصلاحه في 1.4.

المشكلة هي أنه في وقت البناء، أ Thread تتم إضافته إلى قائمة المراجع في جدول المواضيع الداخلية.لن تتم إزالته من تلك القائمة حتى تكتمل طريقة start() الخاصة به.وطالما أن هذا المرجع موجود، فلن يتم جمع البيانات المهملة.

لذا، لا تقم أبدًا بإنشاء موضوع إلا إذا كنت ستتصل به بالتأكيد start() طريقة.أ Thread أشياء run() لا ينبغي أن يتم استدعاء الأسلوب مباشرة.

أفضل طريقة للتشفير هي تنفيذ Runnable واجهة بدلا من فئة فرعية Thread.عندما لا تحتاج إلى موضوع، اتصل

myRunnable.run();

عندما تحتاج إلى موضوع:

Thread myThread = new Thread(myRunnable);
myThread.start();

نصائح أخرى

أشك في أن إنشاء مثيل لسلسلة رسائل أو فئة فرعية منها يؤدي إلى تسرب الذاكرة.أولاً، لا يوجد شيء من الأنواع المذكورة في Javadocs أو مواصفات لغة Java.ثانيًا، أجريت اختبارًا بسيطًا وأظهر أيضًا عدم وجود أي تسرب للذاكرة (على الأقل ليس في JDK 1.5.0_05 من Sun على الإصدار 32 بت x86 Linux 2.6):

public final class Test {
  public static final void main(String[] params) throws Exception {
    final Runtime rt = Runtime.getRuntime();
    long i = 0;
    while(true) {
      new MyThread().run();
      i++;
      if ((i % 100) == 0) {
        System.out.println((i / 100) + ": " + (rt.freeMemory() / 1024 / 1024) + " " + (rt.totalMemory() / 1024 / 1024));
      }
    }
  }

  static class MyThread extends Thread {
    private final byte[] tmp = new byte[10 * 1024 * 1024];

    public void run() {
      System.out.print(".");
    }
  }
}

يحرر:فقط لتلخيص فكرة الاختبار أعلاه.يشير كل مثيل للفئة الفرعية MyThread لسلسلة الرسائل إلى صفيف خاص به يبلغ 10 ميجابايت.إذا لم يتم جمع مثيلات MyThread من القمامة، فسوف تنفد ذاكرة JVM بسرعة كبيرة.ومع ذلك، فإن تشغيل رمز الاختبار يوضح أن JVM يستخدم مقدارًا ثابتًا صغيرًا من الذاكرة بغض النظر عن عدد MyThreads الذي تم إنشاؤه حتى الآن.أدعي أن هذا يرجع إلى أن مثيلات MyThread يتم جمع القمامة.

دعونا نرى ما إذا كان بإمكاننا الاقتراب من جوهر المشكلة:

إذا بدأت برنامجك (على سبيل المثال) 1000 x باستخدام start()، ثم 1000 x باستخدام run() في سلسلة رسائل، فهل يفقد كلاهما الذاكرة؟إذا كان الأمر كذلك، فيجب التحقق من الخوارزمية الخاصة بك (أي.للكائنات الخارجية مثل المتجهات المستخدمة في Runnable).

إذا لم يكن هناك تسرب للذاكرة كما هو موضح أعلاه، فيجب عليك التحقق من معلمات البدء واستخدام الذاكرة لسلاسل العمليات المتعلقة بـ JVM.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top