В каких случаях может возникнуть утечка памяти
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Утечки памяти в Android: основные причины и механизмы
Утечки памяти в Android возникают, когда объекты удерживаются в памяти дольше необходимого, препятствуя их сборке Garbage Collector'ом (GC). Это приводит к повышенному потреблению памяти, частым срабатываниям GC, "тормозам" в интерфейсе и, в худшем случае, к фатальной ошибке OutOfMemoryError.
Ключевые категории причин
1. Долгоживущие ссылки на контекст (Context)
Наиболее частая причина — удержание ссылки на Activity или Context после его уничтожения.
class LeakySingleton {
companion object {
private var activityContext: Context? = null
fun setContext(context: Context) {
activityContext = context // УТЕЧКА! Singleton переживает Activity
}
}
}
Правильно: хранить ApplicationContext или использовать слабые ссылки.
2. Неотменённые слушатели (Listeners) и колбэки
Регистрация слушателей без отмены при уничтожении компонента.
class MyActivity : AppCompatActivity() {
private val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
private val sensorListener = MySensorListener()
override fun onResume() {
super.onResume()
sensorManager.registerListener(sensorListener, ...)
}
// УТЕЧКА! Не отменена регистрация в onPause()
}
3. Внутренние классы и анонимные классы
Нестатические внутренние классы неявно содержат ссылку на внешний класс.
public class OuterActivity extends Activity {
private InnerHandler mHandler = new InnerHandler(); // УТЕЧКА!
// Нестатический внутренний класс
private class InnerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// Имеет неявную ссылку на OuterActivity
}
}
}
Исправление: объявить класс как static и передавать слабую ссылку.
4. Колбэки в сторонних библиотеках
Библиотеки, требующие передачи this (Activity), часто сохраняют эту ссылку.
// Некоторые рекламные SDK или analytics-библиотеки
AdLibrary.getInstance().init(this) // Может удерживать Activity
5. Долгоживущие background-потоки и корутины
Фоновые задачи, работающие дольше жизненного цикла UI-компонента.
GlobalScope.launch { // Глобальная область видимости!
delay(10000)
updateUI() // Попытка обновить уничтоженную Activity
}
Решение: использовать lifecycleScope или viewModelScope.
6. Работа с View и Drawable
class LeakyAdapter : RecyclerView.Adapter<...>() {
private val drawables = mutableListOf<Drawable>()
fun addDrawable(drawable: Drawable) {
drawables.add(drawable) // Drawable может содержать ссылку на View/Context
}
}
7. Архитектурные компоненты без очистки
Использование ViewModel или других компонентов, сохраняющих ссылки на данные.
class MyViewModel : ViewModel() {
private val largeDataList = mutableListOf<ByteArray>() // Большие данные
fun clearData() {
largeDataList.clear() // Если не вызывать, данные живут с ViewModel
}
}
Техники обнаружения и предотвращения
Инструменты мониторинга:
- Android Profiler в Android Studio (Memory tab)
- LeakCanary — автоматическое обнаружение утечек
- MAT (Memory Analyzer Tool) для глубокого анализа дампов памяти
Лучшие практики:
- Используйте Application Context везде, где достаточно
- Отменяйте регистрацию слушателей в соответствующих методах жизненного цикла
- Используйте WeakReference для ссылок, которые могут пережить компонент
- Очищайте коллекции и кэши при необходимости
- Используйте правильные скоупы для корутин
- Избегайте статических полей, содержащих контекст
// Безопасная работа с контекстом
class SafeManager(private val appContext: Context) {
companion object {
private var instance: SafeManager? = null
fun init(context: Context) {
if (instance == null) {
instance = SafeManager(context.applicationContext)
}
}
}
}
Понимание этих паттернов помогает создавать стабильные приложения с предсказуемым потреблением памяти. Регулярный профилинг и использование автоматизированных инструментов обнаружения утечек должны быть частью процесса разработки каждого Android-приложения.