Те, що ви задаєте, - досить складне питання. Хоча ви можете подумати, що це лише одне запитання, ви насправді задаєте відразу кілька питань. Я зроблю все можливе з того, що я маю це висвітлити, і, сподіваюся, деякі інші приєднаються, щоб висвітлити те, що я можу пропустити.
Вкладені класи: Вступ
Оскільки я не впевнений, наскільки вам комфортно з OOP на Java, це вплине на пару основ. Вкладений клас - це коли визначення класу міститься в іншому класі. В основному існує два типи: Статичні вкладені класи та Внутрішні класи. Справжня різниця між ними:
- Статичні вкладені класи:
- Вважаються "найвищим рівнем".
- Не потрібно будувати екземпляр класу, що містить.
- Не може посилатися на членів класу без явного посилання.
- Мають своє життя.
- Внутрішні вкладені класи:
- Завжди потрібно побудувати екземпляр класу, що містить.
- Автоматично мати неявну посилання на містить екземпляр.
- Можливий доступ до членів класу контейнера без посилання.
- Термін експлуатації повинен бути не довший, ніж у контейнера.
Збір сміття та внутрішні класи
Збір сміття є автоматичним, але намагається видалити об'єкти залежно від того, чи вважає вони, що вони використовуються. Колекціонер сміття досить розумний, але не бездоганний. Він може лише визначити, чи використовується щось за допомогою активного посилання на об'єкт чи ні.
Справжня проблема тут полягає в тому, коли внутрішній клас зберігається живим довше, ніж його контейнер. Це пояснюється неявним посиланням на клас, що містить. Єдиний спосіб цього може відбутися, якщо об’єкт поза класом, що містить, зберігає посилання на внутрішній об'єкт, не враховуючи об'єкт, що містить.
Це може призвести до ситуації, коли внутрішній об’єкт живий (за допомогою посилання), але посилання на об'єкт, що міститься, вже видалено з усіх інших об'єктів. Отже, внутрішній об'єкт підтримує живий об'єкт живим, оскільки він завжди матиме посилання на нього. Проблема в цьому полягає в тому, що, якщо він не запрограмований, немає способу повернутися до об'єкта, який міститься, щоб перевірити, чи він ще живий.
Найважливіший аспект цієї реалізації полягає в тому, що це не має значення, чи є воно у діяльності, чи є простим. Ви завжди повинні бути методичними при використанні внутрішніх класів і стежте, щоб вони ніколи не переживали об'єкти контейнера. На щастя, якщо він не є основним об'єктом вашого коду, витоки можуть бути невеликими порівняно. На жаль, це деякі найскладніші витоки, які можна знайти, оскільки вони, ймовірно, залишаться непоміченими, поки багато з них не просочуються.
Рішення: Внутрішні класи
- Отримайте тимчасові посилання від містить об'єкта.
- Дозвольте що містить об'єкт єдиним, щоб зберігати довговічні посилання на внутрішні об'єкти.
- Використовуйте встановлені схеми, такі як Фабрика.
- Якщо внутрішній клас не потребує доступу до членів класу, що містить, розгляньте його перетворення в статичний клас.
- Використовуйте обережно, незалежно від того, входить він у діяльність чи ні.
Діяльність та погляди: Вступ
Діяльність містить багато інформації, яку можна запускати та показувати. Діяльність визначається характеристикою того, що вони повинні мати Вигляд. Вони також мають певні автоматичні обробники. Незалежно від того, вказали ви це чи ні, у "Активісті" є неявна посилання на перегляд, який він містить.
Для того, щоб створити вигляд, він повинен знати, де його створити та чи є у нього діти, щоб він міг відображатись. Це означає, що кожен перегляд має посилання на активність (через getContext()
). Більше того, кожен погляд зберігає посилання на своїх дітей (тобто getChildAt()
). Нарешті, кожен перегляд зберігає посилання на відтворену растрову карту, яка представляє його відображення.
Щоразу, коли у вас є посилання на активність (або контекст діяльності), це означає, що ви можете слідувати ланцюжку ENTIRE вниз по ієрархії макета. Ось чому витоки пам’яті щодо «Діянь» або «Переглядів» - це така велика справа. Це може випустити тону пам’яті всі відразу.
Заходи, види та внутрішні класи
З огляду на вищенаведену інформацію про Внутрішні класи, це найчастіші витоки пам'яті, але також найбільш часто їх уникати. Хоча внутрішній клас бажано мати прямий доступ до членів класу Діяльність, багато хто готовий просто зробити їх статичними, щоб уникнути можливих проблем. Проблема з видами діяльності та переглядами йде набагато глибше.
Витік діяльності, погляди та контекст діяльності
Все зводиться до контексту та життєвого циклу. Існують певні події (наприклад, орієнтація), які вбивають контекст діяльності. Оскільки так багато класів і методів вимагає Контексту, розробники іноді намагатимуться зберегти якийсь код, захопивши посилання на контекст і тримаючись за нього. Так буває, що багато об'єктів, які ми повинні створити для запуску нашої діяльності, повинні існувати поза життєвим циклом діяльності, щоб дозволити діяльності робити те, що їй потрібно робити. Якщо будь-який із ваших об’єктів має посилання на активність, її контекст або будь-який із його переглядів, коли він знищений, ви просто просочили цю активність та все дерево перегляду.
Рішення: Діяльність та погляди
- За будь-яку ціну уникайте статичних посилань на вигляд або діяльність.
- Усі посилання на контексти діяльності повинні бути короткочасними (тривалість функції)
- Якщо вам потрібен довговічний контекст, використовуйте контекст програми (
getBaseContext()
або getApplicationContext()
). Вони не містять посилань неявно.
- Крім того, ви можете обмежити знищення діяльності, змінивши конфігураційні зміни. Однак це не заважає іншим потенційним подіям руйнувати Діяльність. Хоча ви можете це зробити, можливо, ви все ще захочете посилатися на вищезазначені практики.
Випускні: Вступ
Бігові майданчики насправді не такі вже й погані. Я маю на увазі, вони могли бути, але насправді ми вже потрапили в більшість небезпечних зон. Runnable - це асинхронна операція, яка виконує завдання, незалежне від потоку, на якому було створено. Більшість застосунків використовуються з потоку інтерфейсу користувача. По суті, використання Runnable - це створення ще однієї нитки, просто трохи більш керованої. Якщо ви класифікуєте Runnable, як стандартний клас, і дотримуєтесь наведених вище вказівок, у вас виникне проблема. Реальність така, що багато розробників цього не роблять.
З-за простоти, читабельності та логічного потоку програми багато розробників використовують Anonymous Inner Clas для визначення своїх Runnables, наприклад, наведеного вище прикладу. Це призводить до такого прикладу, як той, який ви ввели вище. Анонімний внутрішній клас - це в основному дискретний внутрішній клас. Вам просто не потрібно створювати абсолютно нове визначення і просто переосмислювати відповідні методи. В усіх інших відношеннях це Внутрішній клас, що означає, що він зберігає неявну посилання на свій контейнер.
Випускні та заняття / Перегляди
Так! Цей розділ може бути коротким! Через те, що Runnables працює за межами поточної нитки, небезпека при цьому виникає при тривалих асинхронних операціях. Якщо функція, яку можна виконати, визначена в розділі Діяльність або Перегляд як анонімний внутрішній клас АБО вкладений Внутрішній клас, існують деякі дуже серйозні небезпеки. Це відбувається тому , що, як було сказано раніше, він повинен знати , хто його контейнер. Введіть зміну орієнтації (або вбивство системи). Тепер просто зверніться до попередніх розділів, щоб зрозуміти, що саме сталося. Так, ваш приклад досить небезпечний.
Рішення: Виконання
- Спробуйте розширити Runnable, якщо це не порушує логіку вашого коду.
- Зробіть все можливе, щоб зробити розширені Runnables статичними, якщо вони повинні бути вкладеними класами.
- Якщо вам потрібно використовувати Anonymous Runnables, уникайте їх створення в будь-якому об'єкті, який має довговічне посилання на активність або перегляд, який використовується.
- Багато Runnables так само легко могли бути AsyncTasks. Подумайте про використання AsyncTask, оскільки вони за замовчуванням управляються VM.
Відповідь на остаточне запитання
Тепер, щоб відповісти на запитання, на які безпосередньо не зверталися інші розділи цієї публікації. Ви запитали "Коли об'єкт внутрішнього класу може вижити довше, ніж його зовнішній клас?" Перш ніж ми дістанемося до цього, дозвольте мені переосмислити: хоча ви правильно турбуєтесь про це в діяльності, це може спричинити витік в будь-якому місці Я наведіть простий приклад (без використання діяльності) просто для демонстрації.
Нижче наведено загальний приклад базової фабрики (відсутній код).
public class LeakFactory
{//Just so that we have some data to leak
int myID = 0;
// Necessary because our Leak class is an Inner class
public Leak createLeak()
{
return new Leak();
}
// Mass Manufactured Leak class
public class Leak
{//Again for a little data.
int size = 1;
}
}
Це не настільки поширений приклад, але досить простий для демонстрації. Ключовим тут є конструктор ...
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Gotta have a Factory to make my holes
LeakFactory _holeDriller = new LeakFactory()
// Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//Store them in the class member
myHoles[i] = _holeDriller.createLeak();
}
// Yay! We're done!
// Buh-bye LeakFactory. I don't need you anymore...
}
}
Тепер у нас є витоки, але немає заводу. Незважаючи на те, що ми випустили Фабрику, вона залишиться в пам’яті, оскільки кожен витік має посилання на неї. Навіть не важливо, що зовнішній клас не має даних. Це трапляється набагато частіше, ніж можна було б подумати. Нам не потрібен творець, лише його творіння. Тому ми створюємо його тимчасово, але використовуємо твори нескінченно.
Уявіть, що відбувається, коли ми дещо змінимо конструктор.
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//WOW! I don't even have to create a Factory...
// This is SOOOO much prettier....
myHoles[i] = new LeakFactory().createLeak();
}
}
}
Тепер кожен з цих нових LeakFactories тільки що просочився. Що ти думаєш про це? Це два дуже поширені приклади того, як внутрішній клас може переживати зовнішній клас будь-якого типу. Якби цей зовнішній клас був Діяльністю, уявіть, наскільки це було б гірше.
Висновок
У них перераховані основні небезпеки небезпечного використання цих об'єктів. Взагалі ця публікація повинна була охоплювати більшість ваших питань, але я розумію, що це публікація в лунгу, тому якщо вам потрібно уточнення, просто дайте мені знати. Поки ви будете дотримуватися вищезазначених практик, ви будете дуже мало турбуватися про витік.