تطبيق Android في الذاكرة. تقرير التحسين لـ Yandex.Luncher

زاد نظام Android Go خفيف الوزن من متطلبات التطبيقات المثبتة مسبقًا - الحجم والذاكرة المستخدمة. واجهنا التحدي المتمثل في تلبية هذه المتطلبات. لقد نفذنا عددًا من التحسينات وقررنا تغيير هيكل قشرة الرسوم البيانية بجدية - Yandex.Luncher. شارك رئيس فريق تطوير تطبيقات الهاتف المحمول ألكسندر ستارتشينكو هذه التجربة.


- اسمي ألكساندر ، أنا من سان بطرسبرج ، من الفريق الذي يطور Yandex.Loncher و Yandex.Phone. سأخبرك اليوم كيف قمنا بتحسين الذاكرة في Launcher. أولاً ، سأشرح بإيجاز ما هو Launcher. بعد ذلك ، نناقش الأسباب التي تجعلنا بحاجة إلى تحسين الذاكرة. بعد ذلك ، سوف ننظر في كيفية قياس الذاكرة بشكل صحيح وما تتكون منه. ثم دعنا ننتقل إلى الممارسة. سأتحدث عن كيفية تحسين الذاكرة في Launcher وكيف توصلنا إلى حل جذري للمشكلة. وفي النهاية سأتحدث عن كيفية مراقبة استخدام الذاكرة ، وكيفية إبقائها تحت السيطرة.



"المشغل" أو "المشغل" - ليس مهمًا جدًا. اعتدنا في Yandex على إطلاقه على Launcher ، وفي التقرير سأستخدم كلمة "Launcher".



نقطة مهمة أخرى: يتم توزيع Launcher على نطاق واسع من خلال الإعدادات المسبقة ، أي عند شراء هاتف جديد ، يمكن أن يتحول Yandex.Loncher غالبًا إلى مدير التطبيقات الوحيد ، مدير سطح المكتب المنزلي على هاتفك.

الآن للأسباب التي تجعلنا بحاجة إلى تحسين الذاكرة. سأبدأ مع السبب لدينا. باختصار ، هذا هو Android Go. والآن أطول. في نهاية عام 2017 ، قدمت Google Android Oreo ونسخته الخاصة ، إصدار Android Oreo Go. ما هو خاص حول؟ تم تصميم هذا الإصدار من أجل الأجهزة منخفضة التكلفة ، والهواتف الرخيصة التي تصل سعتها إلى واحد غيغابايت من ذاكرة الوصول العشوائي. ماذا هي خاصة؟ بالنسبة للتطبيقات المثبتة مسبقًا على هذا الإصدار من Android ، تقدم Google متطلبات إضافية. على وجه الخصوص - متطلبات استهلاك ذاكرة الوصول العشوائي. وبشكل تقريبي ، تتم إزالة ذاكرة التطبيق ، بعد مرور بعض الوقت على بدء التشغيل ، ويجب ألا يتجاوز الحجم ما بين 30 و 50 ميغابايت لـ Launcher ، وذلك وفقًا لحجم شاشة الهاتف. 30 على الأصغر و 50 على الشاشات الكبيرة.

تجدر الإشارة أيضًا إلى أن Google تواصل تطوير هذه المنطقة ، وهناك بالفعل إصدار Android Pie Go.

ما هي الأسباب الأخرى التي يمكن أن يكون هناك لتحسين استخدام الذاكرة؟ أولاً ، سيكون تنزيل التطبيق أقل احتمالًا. ثانياً ، ستعمل بشكل أسرع ، لأنه سيكون أقل احتمالًا للعمل على أداة تجميع مجمعي البيانات المهملة وسيتم تخصيص ذاكرة أقل كثيرًا. لن يتم إنشاء كائنات إضافية ، ولن يتم تضخيم طرق العرض الإضافية ، وما إلى ذلك. وبشكل غير مباشر ، بناءً على تجربتنا ، سيؤدي ذلك إلى انخفاض في حجم ملف apk لتطبيقك. سيوفر لك كل هذا معًا المزيد من عمليات التثبيت وتقييمات أفضل على Google Play.

حسنًا ، نحن نعرف الآن سبب تحسين الذاكرة. دعونا نرى ما يعني لقياسه وما يتكون منه.

رابط من الشريحة

ربما رأى الكثير منكم هذه الصورة. هذه لقطة شاشة من Android Studio Profile ، من طريقة عرض الذاكرة. تم وصف هذه الأداة بتفاصيل كافية على developer.android.com. ربما استخدمها الكثير منكم. الذي لم يستخدم - حاول.

ما هو جيد هنا؟ هو دائما في متناول اليد. أنها مريحة للاستخدام في عملية التطوير. ومع ذلك ، فقد بعض العيوب. ليست كل مخصصات التطبيق مرئية هنا. على سبيل المثال ، الخطوط التي تم تنزيلها غير مرئية هنا. أيضًا ، بمساعدة هذه الأداة ، من غير المريح معرفة الفئات التي يتم تحميلها في الذاكرة ، ولا يمكنك استخدام هذه الأداة في الوضع التلقائي ، أي أنه لا يمكنك تكوين نوع من الاختبار التلقائي استنادًا إلى Android Studio Profile.

روابط من الشريحة: الأولى والثانية

الأداة التالية موجودة منذ تطوير Android في Eclipse ، وهو Memory Analyzer ، MAT ، باختصار. يتم توفيره كتطبيق مستقل ومتوافق مع مقالب الذاكرة التي يمكنك حفظها من Android Studio.

للقيام بذلك ، سوف تحتاج إلى استخدام أداة صغيرة ، محول محترف. إنه يأتي مع إصدار Android Go وله العديد من المزايا. على سبيل المثال ، يمكنه إنشاء مسارات لجذور gs. لقد ساعدنا ذلك كثيرًا على معرفة الفئات التي يتم تحميلها من قِبل Launcher ومتى يتم تحميلها. لم نتمكن من القيام بذلك باستخدام Android Studio Profiler.



الأداة التالية هي الأداة المساعدة dumpsys ، وتحديدا dumpsys meminfo. هنا ترى جزءًا من إخراج هذا الأمر. أنه يوفر معرفة رفيعة المستوى إلى حد ما من استهلاك الذاكرة. ومع ذلك ، فقد مزايا معينة. أنها مريحة للاستخدام في الوضع التلقائي. يمكنك تكوين الاختبارات بسهولة التي تستدعي هذا الأمر. كما يعرض الذاكرة على الفور لجميع العمليات. ويظهر كل المواقع. بقدر ما نعلم ، تستخدم Google قيمة الذاكرة من هذه الأداة في عملية الاختبار.

دعنا نستخدم مثالاً للإخراج لوصف موجز لما تتكون عليه ذاكرة التطبيق. الأول هو Java Heap ، وجميع مواقع كود Java و Kotlin. عادة ما يكون هذا القسم كبيرًا بدرجة كافية. يصل المقبل هو كومة الأم. فيما يلي تخصيصات من الكود الأصلي. حتى إذا لم تكن تستخدم الشفرة الأصلية بشكل صريح في تطبيقك ، فستكون التخصيصات موجودة هنا ، لأن العديد من كائنات Android - نفس العرض - تخصص ذاكرة أصيلة. القسم التالي هو الكود. كل ما يتعلق بالرمز يحصل هنا: bytecode ، الخطوط. يمكن أن تكون الشفرة أيضًا كبيرة جدًا إذا كنت تستخدم العديد من مكتبات الجهات الخارجية غير المحسّنة. فيما يلي مجموعة برامج Java البرمجية والرمز الأصلي ، وعادةً ما تكون صغيرة. التالي يأتي في الذاكرة الرسومية. يتضمن ذلك السطح ، والقوام ، أي أن الذاكرة التي تنتشر بين وحدة المعالجة المركزية ووحدة معالجة الرسومات يتم استخدامها للتجسيد. التالي هو القسم الخاص الآخر. يتضمن هذا كل ما لم يندرج في الأقسام أعلاه ، كل ما لم يتمكن النظام من توزيعه. عادة ما تكون هذه بعض أنواع التخصيصات الأصلية. التالي هو قسم النظام ، وهذا هو جزء من ذاكرة النظام التي تنسب إلى التطبيق الخاص بك.

وفي النهاية لدينا TOTAL ، هذا هو مجموع جميع الأقسام المدرجة. نحن نريد للحد منه.



ماذا المهم أن تعرف عن قياس الذاكرة؟ بادئ ذي بدء ، لا يتحكم تطبيقنا بشكل كامل في جميع التخصيصات. أي أننا ، كمطورين ، لا نملك السيطرة الكاملة على الكود الذي سيتم تنزيله.

ما يلي. ذاكرة التطبيق يمكن أن تقفز كثيرا. أثناء عملية القياس ، يمكنك ملاحظة اختلافات قوية في القراءات. قد يكون هذا بسبب الوقت المستغرق وكذلك في العديد من السيناريوهات. في هذا الصدد ، عندما نقوم بتحسين الذاكرة ، نحللها ، من المهم جدًا القيام بذلك في ظل نفس الظروف. من الناحية المثالية ، على نفس الجهاز. حتى لو كان لديك الخيار للاتصال بـ Garbage Collector.

ممتاز. نحن نعرف لماذا نحتاج إلى تحسين الذاكرة ، وكيفية قياسها بشكل صحيح ، وما هي مكوناتها. دعنا نمارس ، وسأخبرك كيف قمنا بتحسين الذاكرة في Launcher.



كان هذا هو الوضع في البداية. كان لدينا ثلاث عمليات ، والتي في المجموع خصصت حوالي 120 ميغابايت. هذا هو ما يقرب من أربعة أضعاف ما نود الحصول عليه.



فيما يتعلق بتخصيص العملية الرئيسية ، كان هناك قسم كبير من Java Heap ، والكثير من الرسومات ، والكود الكبير ، و Native Heap كبير إلى حد ما.



أولاً ، لقد تعاملنا مع المشكلة بسذاجة وقرّرنا اتباع بعض التوصيات من Google من بعض الموارد ، وحاول حل المشكلة بسرعة. لفتنا الانتباه إلى الأساليب الاصطناعية التي يتم إنشاؤها أثناء عملية التجميع. كان لدينا أكثر من 2000 منهم. في بضع ساعات ، ونحن جميعا حذفها ، وإزالة الذاكرة.



وحصلوا على حوالي واحد أو اثنين ميغابايت في قسم التعليمات البرمجية. ممتاز.

بعد ذلك ، تحولنا انتباهنا إلى التعداد. كما تعلمون ، يعد التعداد فئة. وكما اعترفت Google في نهاية المطاف ، فإن التعداد ليس فعالًا جدًا في الذاكرة. لقد ترجمنا كل التعداد إلى InDef و StringDef. هنا يمكنك الاعتراض على أن ProgArt ستساعد هنا. ولكن في الواقع ، لن تحل ProgArt محل جميع التعدادات بأنواع بدائية. من الأفضل أن تفعل ذلك بنفسك. بالمناسبة ، كان لدينا أكثر من 90 التعداد ، والكثير جدا.



استغرق هذا التحسين أيامًا بالفعل ، حيث كان يجب إجراء معظمه يدويًا ، وفزنا بحوالي 3 إلى 6 ميغابايت في قسم كومة Java.

بعد ذلك ، لفتنا الانتباه إلى المجموعة. استخدمنا مجموعات جافا القياسية إلى حد ما ، مثل HashMap. كان لدينا أكثر من 150 منهم ، وجميعهم تم إنشاؤها في بداية Launcher. استبدلناهم بـ SparseArray و SimpleArrayMap و ArrayMap وبدأنا في إنشاء مجموعات بحجم محدد مسبقًا حتى لا يتم تخصيص فتحات فارغة. أي أننا نمرر حجم المجموعة إلى المنشئ.



لقد حقق هذا أيضًا مكسبًا معينًا ، واستغرق هذا التحسين أيضًا أيامًا ، معظمنا فعلناها يدويًا.

ثم اتخذنا خطوة أكثر تحديدا. رأينا أن لدينا ثلاث عمليات. كما نعلم ، حتى العملية الفارغة في Android تستغرق حوالي 8-10 ميغابايت من الذاكرة ، والكثير جدًا.

أخبر زميلي آرثر فاسيلوف تفاصيل العمليات. منذ وقت ليس ببعيد في مؤتمر Mosdroid كان تقريره ، وكذلك عن Android Go.



ماذا لدينا بعد هذه التحسينات؟ على جهاز الاختبار الرئيسي ، لاحظنا استهلاك الذاكرة في المنطقة من 80-100 ميغابايت ، ليس سيئًا بدرجة كافية ، ولكن لا يزال غير كافٍ. بدأنا في قياس الذاكرة على الأجهزة الأخرى. وجدنا أنه على الأجهزة الأسرع ، كان استهلاك الذاكرة أكبر بكثير. اتضح أن لدينا العديد من التهيئة المعلقة المختلفة. بعد مرور بعض الوقت ، قام Launcher بتضخيم بعض المشاهدات ، وبدء بعض المكتبات ، إلخ.



ماذا فعلنا؟ بادئ ذي بدء ، ذهبنا من خلال الرأي ، جميع التصاميم. تمت إزالة جميع المشاهدات التي تضخمت مع وضوح الرؤية. جلبوا لهم في تخطيطات منفصلة ، وبدأ في تضخيم لهم برمجيا. تلك التي لم نكن بحاجة إليها ، توقفنا عمومًا عن التضخم حتى اللحظة التي احتاجها المستخدم إليها. لقد اهتمنا بتحسين الصورة. توقفنا عن تحميل الصور التي لا يراها المستخدم في الوقت الحالي. في حالة Launcher ، كانت هذه أيقونات صور للتطبيقات في القائمة الكاملة للتطبيقات. حتى افتتاحه ، نحن لا شحنهم. لقد أعطانا هذا فوزًا جيدًا في قسم الرسومات.

لقد فحصنا أيضًا ذاكرة التخزين المؤقت لصورنا في الذاكرة. اتضح أنه لم تكن جميعها مثالية ؛ لم يتم تخزين جميع الصور المطابقة لشاشة الهاتف الذي كان يعمل عليه Launcher في الذاكرة.

بعد ذلك ، بدأنا في تحليل قسم الكود ولاحظنا أن لدينا الكثير من الفصول الثقيلة إلى حد ما من مكان ما. اتضح أن هذه هي في معظمها فصول المكتبة. وجدنا بعض الأشياء الغريبة في بعض المكتبات. قامت إحدى المكتبات بإنشاء HashMap وفي مُهيئ ثابت تمسك به بعدد كبير من الكائنات.



مكتبة أخرى أيضا تحميل الملفات الصوتية في كتلة ثابتة ، والتي تشغل حوالي 700 كيلو بايت من الذاكرة.



لقد توقفنا عن تهيئة هذه المكتبات ، وبدأنا العمل معها فقط عندما يحتاج المستخدمون إلى هذه الوظائف حقًا. استغرقت كل هذه التحسينات عدة أسابيع. لقد اختبرنا الكثير ، وتحققنا من أننا لم نعرض مشاكل إضافية. لكننا حققنا أيضًا فوزًا جيدًا ، حوالي 25 من 40 ميغابايت في أقسام Native و Heap و Code و Java Heap.

لكن هذا لم يكن كافيا. لم ينخفض ​​استهلاك الذاكرة إلى 30 ميغابايت. يبدو أننا استنفدنا جميع الخيارات لبعض التحسينات التلقائية والآمنة البسيطة.

قررنا التفكير في حلول جذرية. رأينا هنا خيارين - إنشاء تطبيق لايت منفصل أو معالجة بنية Launcher والانتقال إلى بنية نمطية مع القدرة على إنشاء Launcher بدون وحدات إضافية. الخيار الأول طويل ومكلف للغاية. على الأرجح ، سينتج عن إنشاء هذا التطبيق تطبيق منفصل كامل عنك ، والذي سوف يحتاج إلى دعم وتطوير كاملين. من ناحية أخرى ، يعد الخيار ذو البنية النمطية مكلفًا للغاية ومحفوفًا بالمخاطر ، ولكنه لا يزال أسرع ، نظرًا لأنك تعمل بالفعل باستخدام قاعدة رمز مشهورة ، لديك بالفعل مجموعة من اختبارات الوحدات التلقائية واختبارات التكامل والاختبارات اليدوية -keysov.

تجدر الإشارة إلى أنه بغض النظر عن الخيار الذي تختاره ، فسيتعين عليك بطريقة ما التخلي عن جزء من ميزات التطبيق الخاص بك في إصدار Android Go. هذا طبيعي. تفعل Google نفس الشيء في تطبيقات Go.

نتيجةً لذلك ، بعد تطبيق بنية معيارية ، قمنا بحل مشكلات ذاكرتنا بشكل موثوق واعتمدنا على اجتياز الاختبارات حتى على الأجهزة ذات الشاشة الصغيرة ، أي خفض استهلاك الذاكرة إلى 30 ميغابايت.



قليلا عن مراقبة الذاكرة ، حول كيفية الحفاظ على استخدام الذاكرة تحت السيطرة. بادئ ذي بدء ، أنشأنا أجهزة تحليل ثابتة ، وهي نفس أداة الوبر على الخطأ في الحالات التي نستخدم فيها التعداد أو إنشاء طرق تركيبية أو استخدام مجموعات غير محسنة.

كذلك أكثر صعوبة. لقد قمنا بإعداد اختبارات تكامل تلقائية تقوم بتشغيل Launcher على أجهزة محاكاة وبعد فترة من الوقت تقلع عن استهلاك الذاكرة. إذا كان مختلفًا تمامًا عن البنية السابقة ، يتم تشغيل التحذيرات والتنبيهات. ثم نبدأ في استكشاف المشكلة وعدم نشر التغييرات التي تزيد من استخدام ذاكرة المشغل.

لتلخيص. هناك العديد من الأدوات لمراقبة الذاكرة وقياس الذاكرة للتشغيل السريع والفعال. من الأفضل استخدامهم جميعًا ، حيث لديهم إيجابياتهم وسلبياتهم.

تحولت الحلول الجذرية ذات البنية المعيارية إلى أن تكون أكثر موثوقية وكفاءة بالنسبة لنا. نأسف لأننا لم نأخذها على الفور. لكن الخطوات التي تحدثت عنها في بداية التقرير لم تذهب سدى. لاحظنا أن الإصدار الرئيسي من التطبيق بدأ في استخدام الذاكرة على النحو الأمثل ، للعمل بشكل أسرع. شكرا لك

Source: https://habr.com/ru/post/ar448966/


All Articles