← Назад к вопросам

Что такое root в Garbage Collector?

3.0 Senior🔥 91 комментариев
#JVM и память

Комментарии (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:

  1. Stack переменные - локальные в методах
  2. Static поля - всегда живые (опасно!)
  3. Активные потоки - существуют пока работает код
  4. Monitor объекты - на которых выполняется synchronized

Объект собирается только если недостижим из любого root'а. Утечка памяти возникает когда объект остается достижимым при долгой жизни root'а.