Java هو نوع من لغة جمع القمامة. مصلحتها هي أن المطورين لا يحتاجون إلى إدارة تخصيص الذاكرة عمداً ، مما يقلل من احتمال تعطل التطبيقات بسبب أخطاء التجزئة المحلية ، ويمنع الذاكرة غير المتجددة من الضغط على المكدس (كومة). لذلك ، فإن الكود المكتوب أكثر أمانًا.
لسوء الحظ ، لا يزال هناك العديد من التسريبات المنطقية في Java معرضة لتسرب الذاكرة. إذا لم تكن حريصًا ، فيمكن أن يضيع تطبيق Android الذاكرة غير المجانية بسهولة ، الأمر الذي سيؤدي في النهاية إلى نفاد خطأ في الذاكرة (خارج الذاكرة ، OOM).
1. سبب تسرب الذاكرة العام هو أنه عندما يتم إصدار جميع الإشارات إلى الكائن ، لم يتم إصدار الكائن. (ملاحظة المترجم: نسيت المؤشر إغلاقه ، إلخ)
2. سبب تسرب الذاكرة المنطقي هو أنه عندما لم يعد التطبيق يحتاج إلى هذا الكائن ، لم يتم إصدار جميع الإشارات إلى الكائن.
إذا كنت تحمل مرجعًا قويًا إلى كائن ، فلا يمكن لمجمع القمامة إعادة تدوير الكائن في الذاكرة.
في تطوير Android ، فإن مشكلة تسرب الذاكرة الأكثر ترجيحًا هي السياق. على سبيل المثال ، يحتوي سياق النشاط على عدد كبير من مراجع الذاكرة ، مثل التسلسلات الهرمية العرض والموارد الأخرى. بمجرد تسرب السياق ، يعني أيضًا تسرب جميع الكائنات التي يشير إليها. تحتوي آلات Android على ذاكرة محدودة ، ويمكن أن تؤدي الكثير من تسرب الذاكرة بسهولة إلى OOM.
يتطلب اكتشاف تسرب الذاكرة المنطقي حكمًا شخصيًا ، وخاصة دورة حياة الكائن غير واضحة. لحسن الحظ ، يتمتع النشاط بدورة حياة واضحة ومن السهل العثور على سبب التسرب. Activity.Ondestroy () يعتبر نهاية حياة النشاط. من الناحية البرمجي ، يجب تدميره ، أو يحتاج نظام Android إلى إعادة تدوير هذه الذاكرة (ملاحظة المترجم: عندما تكون الذاكرة غير كافية ، فإن Android ستعيد تدوير الأنشطة غير المرئية).
إذا تم تنفيذ هذه الطريقة ، فلا يزال هناك إشارة قوية إلى النشاط في المكدس ، ولا يمكن لمجمع القمامة وضع علامة عليها كذاكرة معاد تدويرها ، وهدفنا الأصلي هو إعادة تدويره!
والنتيجة هي أن النشاط يبقى خارج دورة حياته.
النشاط هو كائن الوزن الثقيل ويجب معالجته بواسطة نظام Android. ومع ذلك ، تحدث تسرب الذاكرة المنطقية دائمًا عن غير قصد. (ملاحظة المترجم: لقد جربت ذات مرة نشاطًا تسبب في تسرب ذاكرة 20 مترًا). في Android ، لا يوجد سوى مصائدان تؤديان إلى تسرب ذاكرة محتملة:
المتغير الثابت للعملية العالمية (Process-Global). هذا الوحش الذي يتجاهل حالة التطبيق ويحمل إشارة قوية إلى النشاط.
المواضيع التي تعيش خارج دورة حياة النشاط. لم يتم مسح أي إشارات قوية إلى النشاط.
تحقق مما إذا كنت قد واجهت المواقف التالية.
1. أنشطة ثابتة
يتم تعريف متغير النشاط الثابت في الفصل ، ويتم تعيين مثيل النشاط قيد التشغيل حاليًا لهذا المتغير الثابت.
إذا لم يتم مسح هذا المتغير الثابت بعد نهاية دورة حياة النشاط ، فسيؤدي ذلك إلى تسرب ذاكرة. نظرًا لأن المتغيرات الثابتة تمر عبر دورة حياة هذا التطبيق ، فإن الأنشطة التي تم تسريبها ستكون موجودة دائمًا في عملية التقديم ولن يتم جمعها بواسطة جامع القمامة.
نشاط النشاط الثابت ؛ void setStaticActivity () {Activity = this ؛} عرض sabutton = findViewById (2. وجهات النظر
يمكن أن تحدث مواقف مماثلة في وضع Singleton ، وإذا كان النشاط يستخدم غالبًا ، فمن العملي حفظ مثيل في الذاكرة. كما ذكرنا سابقًا ، فإن إجبار دورة حياة النشاط أمر خطير وغير ضروري للغاية ، ولا يمكن القيام به على أي حال.
حالة خاصة: إذا كانت التهيئة التي تُعرف عن الموارد تستهلك الكثير من الموارد وبقيت دون تغيير خلال دورة حياة النشاط ، فيمكن تحويلها إلى ثابت وتحميل على الهيراتشي في العرض. وبهذه الطريقة ، عندما يتم تدمير النشاط ، يجب إطلاق المورد. (ملاحظة المترجم: لم يتم إصدار الذاكرة في رمز العينة. مجرد طريقة عرض ثابتة ، لكن لا يزال من المستحسن استخدام طريقة العرض الثابت)
عرض ثابت void setStaticView () {view = findViewById (3. فصول
تابع ، على افتراض أن هناك فئة داخلية في النشاط ، فإن القيام بذلك يمكن أن يحسن قابلية القراءة والتغليف. إذا أنشأنا فئة داخلية وعقدت إشارة إلى متغير ثابت ، تهانينا ، فإن تسرب الذاكرة ليس بعيدًا عنك (ملاحظة المترجم: فارغة عند تدميرها ، أم).
كائن ثابت خاص داخلي ؛ void createInnerClass () {class innerclass {} inner = new innerclass () ؛} عرض icbutton = findViewById (واحدة من مزايا الفصول الداخلية هي أنه يمكنهم الوصول إلى الفصول الخارجية. لسوء الحظ ، فإن سبب تسرب الذاكرة هو أن الفئات الداخلية تحمل إشارات قوية إلى مثيلات فئة خارجية.
4. فصول العيوب
وبالمثل ، تحافظ الفصول المجهولة أيضًا على الإشارات إلى الطبقات الخارجية. لذا ، من السهل أن تحدث تسريبات الذاكرة عند تحديد عدم الكشف عن هويتها في نشاطك. عندما تنفذ المهمة غير المتزامنة مهمة تستغرق وقتًا طويلاً في الخلفية ، يتم تدمير النشاط للأسف (ملاحظة المترجم: مخارج المستخدم ، إعادة تدوير النظام) ، لن يتم إعادة تدوير مثيل النشاط الذي تحتفظ به Asynctask بواسطة جامع القمامة حتى يتم إكمال المهمة غير المتزامنة.
void startAsyNctask () {new Asynctask <void ، void ، void> () {Override void void doinbackground (void ... params) {بينما (true) ؛ }} .execute () ؛} super.oncreate (SaveInstanCestate) ؛ setContentView (R.Layout.activity_main) ؛ عرض aicbutton = findViewById ( NextActivity () ؛5. Handler
وعلى نفس المنوال ، حدد مجهول الهوية وتنفيذه باستخدام معالج فئة مجهول. ستعقد الفئة الداخلية القابلة للتشغيل مرجعًا ضمنيًا إلى الفئة الخارجية ويتم تمريرها إلى رسالة قائمة انتظار رسالة معالج. لن يتم تدمير مثيل النشاط حتى تتم معالجة رسالة الرسالة ، مما يؤدي إلى تسرب الذاكرة.
void createHandler () {new Handler () {Override public void handlemessage (message message) {super.handlemessage (message) ؛ }} .postdelayed (new RunNable () {Override public void run () {بينما (صحيح) ؛}} ، long.max_value >> 1) ؛} عرض hbutton = findViewById ( {createHandler () ؛6.hreads
نعرض مرة أخرى تسرب الذاكرة من خلال الخيط و timertask.
void SpawnThRead () {new thread () {Override public void run () {while (true) ؛ }} .start () ؛} عرض tbutton = findViewById (7.TimerTask
طالما أنها مثيل لفئة مجهولة ، بغض النظر عما إذا كان في مؤشر ترابط العمال أم لا ، فإنه سيحمل إشارة إلى النشاط ، مما يؤدي إلى تسرب الذاكرة.
scheduletimer void () {new timer () void onClick (view v) {scheduletimer () ؛8. مدير المستون
أخيرًا ، يمكن الحصول على خدمات النظام من خلال context.getSystemService (int name). تعمل هذه الخدمات في عملياتها الخاصة ، مما يساعد التطبيقات على التعامل مع مهام الخلفية وتفاعلات الأجهزة. إذا كنت بحاجة إلى استخدام هذه الخدمات ، فيمكنك تسجيل المستمعين ، مما سيؤدي إلى عقد الخدمة إشارة إلى السياق. إذا لم يتم تسجيل هؤلاء المستمعين عند تدمير النشاط ، فسوف يتسبب ذلك في تسرب الذاكرة.
void registerListener () {sensormanager sensormanager = (sensormanager) getSystemService (sensor_service) ؛ مستشعر المستشعر = sensormanager.getDefaultSensor (sensor.type_all) ؛ sensormanager.registerListener (هذا ، sensor ، sensormanager.sensor_delay_fastest) ؛} عرض smbutton = findViewById (لخص
بعد أن رأيت العديد من الأمثلة التي يمكن أن تؤدي إلى تسرب الذاكرة ، من السهل تناول كل ذاكرة هاتفك ، مما يجعل جمع القمامة ومعالجته أكثر ، وحتى في أسوأ الحالات ، سيؤدي إلى OOM. عمليات جمع القمامة باهظة الثمن ويمكن أن تؤدي إلى تأخر مرئي. لذلك ، انتبه إلى السلسلة المرجعية المحتفظ بها عند إنشاء مثيل ، وغالبًا ما تقوم بإجراء عمليات فحص تسرب الذاكرة.