Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Root в Garbage Collector
Root (корень) - это объект, на который есть прямая ссылка, которая не может быть удалена GC автоматически.
Что такое GC Root
GC Root - точки входа для сборки мусора:
- Объекты, которые GC считает всегда живыми
- На основе них GC достраивает граф объектов
- Если объект достижим из какого-то root'а - он живой
- Если объект недостижим из любого root'а - он может быть удален
Типы GC Root'ов
1. Stack Frame (локальные переменные потока)
fun example() {
val obj = MyObject() // obj - GC Root (находится на stack)
println(obj) // obj достижима
} // obj удалется (выходит из scope)
**2. Static Field (статические поля)
class MyClass {
companion object {
val globalRef = MyObject() // GC Root (static - всегда живой)
}
}
// globalRef никогда не будет удален (пока существует класс)
**3. Thread (активный поток)
val thread = Thread {
val obj = MyObject() // obj - GC Root этого потока
// ...
}
thread.start() // Thread существует → obj существует
thread.join() // Thread закончился → obj может быть удален
**4. Monitor (объект, на котором выполняется synchronized)
synchronized(obj) { // obj - GC Root (заблокирован потоком)
// Работа с объектом
} // Освобожден → может быть удален
Граф объектов и достижимость
GC Roots:
├── Stack переменные
├── Static поля
├── Active потоки
└── Monitor объекты
↓
Достижимые объекты (живые)
↓
Недостижимые объекты (мусор → удаляются)
Пример: утечка памяти
class App {
companion object {
val cache = mutableListOf<MyObject>() // GC Root (static)
}
}
fun addToCache() {
val obj = MyObject()
App.cache.add(obj) // obj теперь достижим через static field
} // obj выходит из local scope
// НО объект ВСЕ РАВНО НЕ УДАЛЯЕТСЯ!
// Потому что он в static cache (GC Root)
// Утечка памяти!
Граф объектов в Android
// Activity - GC Root
class MainActivity : AppCompatActivity() {
private val viewModel = MyViewModel() // Достижим через activity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val view = MyView() // Достижима через activity
setContentView(view)
}
}
// При finish() Activity:
// - Activity удаляется (не GC Root больше)
// - viewModel удаляется
// - view удаляется
// - Все их зависимости удаляются
Опасные GC Root'ы
1. Static Reference (опасен!)
class MyView(context: Context) : View(context) {
companion object {
var instance: MyView? = null // GC Root!
}
init {
instance = this // View НИКОГДА не удалится
}
}
// Утечка памяти - activity не может быть сборена
2. Inner Class Reference
class MainActivity : AppCompatActivity() {
inner class MyCallback : Callback { // Неявная ссылка на activity
override fun onResponse(response: Response) {}
}
init {
api.setCallback(MyCallback()) // Callback удерживает activity!
}
}
// РЕШЕНИЕ: использовать WeakReference
class MyCallback(val activity: WeakReference<MainActivity>) : Callback {}
3. Thread Reference
fun startThread() {
val obj = MyObject()
Thread {
while (true) {
println(obj) // obj достижима через thread
Thread.sleep(1000)
}
}.start()
} // obj не удалится пока thread жив!
WeakReference и SoftReference
// WeakReference - может быть собрана GC
var weakRef = WeakReference(obj)
val obj2 = weakRef.get() // null если уже собрана
// SoftReference - собирается если мало памяти
var softRef = SoftReference(obj)
val obj3 = softRef.get() // null если очень мало памяти
// Пример: кеш с WeakReference
class ImageCache {
private val cache = mutableMapOf<String, WeakReference<Bitmap>>()
fun get(key: String): Bitmap? {
return cache[key]?.get() // Может быть null
}
fun put(key: String, bitmap: Bitmap) {
cache[key] = WeakReference(bitmap) // Может быть удален
}
}
Анализ утечек памяти
// Memory Leak: Activity удерживается в памяти
class MyService : Service() {
companion object {
var instance: MyService? = null
}
override fun onCreate() {
super.onCreate()
instance = this // GC Root - утечка!
}
}
// РЕШЕНИЕ: использовать callback'и с WeakReference
class MyService : Service() {
var callback: Callback? = null
override fun onDestroy() {
super.onDestroy()
callback = null // Очистить
}
}
GC Roots в памяти
HEAP:
├── GC Roots (не собираются)
│ ├── Stack переменные
│ ├── Static поля
│ ├── Active потоки
│ └── Monitor объекты
│
├── Живые объекты (достижимы из roots)
│ ├── obj1 (достижима через stack)
│ ├── obj2 (достижима через static field)
│ └── obj3 (достижима через obj1)
│
└── Мусор (недостижимы)
├── obj4 (удалится)
└── obj5 (удалится)
Лучшие практики
✅ Делай так:
- Очищай static references при освобождении
- Используй WeakReference для callback'ов
- Отписывайся от listener'ов
- Очищай inner class references
- Используй Leakcanary для отладки
❌ Избегай:
- Static references на UI объекты
- Inner classes в callback'ах
- Бесконечных потоков
- Удержания context в static
Инструменты отладки
// LeakCanary - автоматическое обнаружение
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.13'
}
// Анализ дампа памяти
// Android Studio → Profiler → Memory → Dump Java Heap
// → Анализ с помощью MAT (Memory Analysis Tool)
Вывод
GC Root - это точка входа для Garbage Collector:
- Stack переменные - локальные в методах
- Static поля - всегда живые (опасно!)
- Активные потоки - существуют пока работает код
- Monitor объекты - на которых выполняется synchronized
Объект собирается только если недостижим из любого root'а. Утечка памяти возникает когда объект остается достижимым при долгой жизни root'а.