Утечки памяти — тихие убийцы производительности приложений. Даже в 2025 году, когда Jetpack Compose доминирует в разработке пользовательских интерфейсов и появляются новые модные инструменты, эти ошибки все еще преследуют разработчиков. Давайте разберем 7 наиболее распространенных виновников и вооружим вас проверенными в бою решениями.
Статический контекст: классическая ловушка
Статические ссылки на Activity
или Context
подобны неживым призракам — они никогда не умирают, блокируя сборку мусора. Часто встречаются в синглтонах или глобальных утилитах.
Всегда инжектируйте контекст приложения (никогда не активити):
object AuthManager { private lateinit var appContext: Context // Initialize in your Application class fun init(application: Application) { appContext = application } fun getUser(): User? { // Use appContext here } } // Try to fix using DI like @Module @InstallIn(SingletonComponent::class) class AppModule { @Provides fun provideContext(@ApplicationContext context: Context): Context = context }
Внутренние классы и лямбды: молчаливые хранители
Нестатические внутренние классы (например, обработчики) или лямбды Compose скрытно хранят родительские ссылки. Ваша активити остается в памяти после поворота экрана? Вот почему:
// For legacy code // Static inner class + WeakReference class MyActivity : AppCompatActivity() { private class SafeHandler( activity: MyActivity ) : Handler(Looper.getMainLooper()) { private val activityRef = WeakReference(activity) override fun handleMessage(msg: Message) { activityRef.get()?.updateUI() } } } // For compose code @Composable fun LeakProofButton(onClick: () -> Unit) { // Captures latest onClick reference val currentOnClick by rememberUpdatedState(onClick) Button(onClick = { currentOnClick() }) { Text("I'm Safe!") } }
Зомби потоки и корутины
Фоновые задачи сохраняются после уничтожения активити/фрагмента. Часто встречаются в disposables RxJava или GlobalScope
.
Корутины: привяжитесь к жизненному циклу с помощью lifecycleScope
:
class MyFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { lifecycleScope.launch { // Auto-cancels when fragment dies fetchData() } } }
Забытые слушатели: фантомные наблюдатели
Незарегистрированные наблюдатели LiveData, broadcast ресиверы или слушатели кликов остаются, как неприятные запахи:
@Composable fun SensorObserver() { val sensorManager = remember { getSystemService(SENSOR_SERVICE) as SensorManager } DisposableEffect(Unit) { val listener = SensorEventListener { /* ... */ } sensorManager.registerListener(listener, ...) onDispose { sensorManager.unregisterListener(listener) // Clean exit! } } }
Спагетти из синглтонов
Синглтоны случайно удерживают ссылки на UI. Видели такую ошибку? MySingleton → MyActivity → MySingleton
(циклическая ссылка!).
- Храните только данные на уровне приложения (пользовательские сессии, конфигурации)
- Инжектируйте контекст через DI (Hilt/Dagger/Koin)
@Singleton class AnalyticsService @Inject constructor( @ApplicationContext private val context: Context ) { ... }
View вампиры: UI-компоненты, которые не хотят умирать
Кастомные представления или ComposeView, сохраняющие ссылки на Activity после отсоединения:
override fun onDetachedFromWindow() { super.onDetachedFromWindow() myView.setOnClickListener(null) // Cut the ties! } // Compose ComposeView(context).apply { setViewCompositionStrategy( ViewCompositionStrategy.DisposeOnDetachedFromWindow ) setContent { /* ... */ } }
Ошибки с растровыми изображениями и монстры состояний
Не отпущенные растровые изображения или массивные состояния Compose, вызывающие OutOfMemoryError
:
AsyncImage( model = "https://example.com/huge_image.jpg", contentDescription = null, modifier = Modifier.fillMaxWidth() ) val heavyList by remember { mutableStateOf(List(10_000) { ... }) } // // Better: Use lazy lists or derivedStateOf val visibleItems by remember { derivedStateOf { heavyList.filter { it.isVisible } } }
Ваш набор инструментов для борьбы с утечками 2025
- LeakCanary 2.14+ — теперь с поддержкой Compose!
- Android Studio Memory Profiler — отслеживание дампов кучи
- Отладчик Compose Recomposition — поиск злодеев состояний