Как реализована дефрагментация памяти в виртуальной машине Android
Что проверяют
Знаете ли вы, как устроена память в ART, а не в абстрактной JVM, и понимаете ли, почему фрагментация была реальной бедой на старых версиях Android. Следом обычно спрашивают, чем разработчик сам может помочь сборщику и как увидеть проблему в профайлере.
Как отвечать
Отдельного процесса «дефрагментации» нет, её выполняет сам сборщик мусора, когда перемещает живые объекты. Историю удобно рассказать по этапам:
- Dalvik: сборщик mark-sweep без перемещения объектов. Куча со временем превращалась в решето, и крупное выделение, например под
Bitmap, падало сOutOfMemoryErrorпри формально свободной памяти; - ART на Android 5-7: основной сборщик CMS тоже не двигает объекты, зато когда приложение уходит в фон, ART уплотняет кучу. Пауза в этот момент пользователю не видна;
- Concurrent Copying с Android 8: куча делится на регионы, живые объекты копируются в новые регионы параллельно с работой приложения благодаря барьерам чтения, так что дефрагментация идёт постоянно. Позже сборщик стал поколенческим и чаще собирает только молодые объекты;
- Concurrent Mark-Compact: в свежих версиях модуля ART, который обновляется через системные обновления Google Play, появился сборщик на основе
userfaultfdбез барьеров чтения и с меньшим расходом памяти.
Добавьте пару деталей. Выделение после уплотнения сводится к сдвигу указателя в локальном буфере потока (TLAB), поэтому оно дешёвое. Крупные массивы примитивов и строки живут в отдельном Large Object Space и не перемещаются. Пиксели Bitmap с Android 8 хранятся в нативной памяти, а не в Java-куче.
Смотреть поведение GC стоит в Memory Profiler Android Studio, в adb shell dumpsys meminfo <пакет> и в строках сборщика в logcat.
Где спотыкаются
Рассказывают про HotSpot: G1, флаги -Xmx и -XX. На Android их нет, приложение не выбирает сборщик и не настраивает кучу.
Смешивают Dalvik и ART и утверждают, что Android никогда не двигает объекты. С Android 8 перемещение идёт постоянно.
Предлагают вызывать System.gc() для «дефрагментации». Это лишь подсказка рантайму, а реальная помощь сборщику в том, чтобы не плодить короткоживущие объекты в onDraw и в циклах прокрутки.