👣 Как работает сборщик мусора в Dart. Память, указатели и smi
Сборщик мусора в Dart - тема, которая часто всплывает на собеседованиях, но найти внятное объяснение непросто. Статьи либо слишком сложные, либо уходят в дебри реализации VM. Давайте разберем основы: как Dart управляет памятью, что такое указатели и почему числа не влияют на производительность.
Биты, байты и указатели:
Память компьютера - это длинный ряд ячеек, каждая из которых содержит бит (0 или 1). Процессоры работают с группами битов. В 64-битных процессорах это группы по 64 бита (8 байт). Это машинное слово.
Указатель в Dart занимает ровно одно машинное слово - 8 байт.
Указатель - это не сам адрес, а место, где этот адрес записан. Если представить адрес дома, то листок бумаги - это указатель, а то, что на нем написано - сам адрес.
Как Dart отличает число от объекта:
Dart числа и объекты хранятся по-разному. Если переменная содержит число, оно хранится прямо в указателе - никакого перехода по адресу не требуется. Если переменная содержит объект, в указателе хранится адрес этого объекта, и чтобы получить доступ к объекту, нужно перейти по этому адресу.
Как Dart понимает, что именно лежит в указателе? Все решает последний бит.
Адреса объектов в памяти кратны 16. Это сделано специально. Если адрес кратен 16, его последние биты всегда нули. Вот это пустующее место Dart использует как флажок.
Последний бит говорит, чем является значение в указателе:
🔹1 - ссылка на объект
🔹0 - число, искать объект по адресу не нужно
Пример: объект лежит по адресу 0x00A03F50. Этот адрес кратен 16. В двоичном виде последний байт: 0101 0000. Dart ставит единицу в конце: 0101 0001 → 0x00A03F51. Теперь это помеченный адрес объекта.
Чтобы дойти до реального объекта, нужно убрать эту единицу и получить исходный адрес.
Smi: числа, которые живут в указателе:
Если число умещается в указателе, оно называется smi (small integer). Smi не занимает места в куче. Оно живет прямо в указателе. Сборщик мусора за ним не следит.
Пример: число 7 = 111 в битах. Когда Dart упаковывает его в smi, он сдвигает биты влево на 1 позицию. Получается 1110. Ноль на конце означает, что это число, а не ссылка. При распаковке число делится на 2, возвращая исходное значение.
Handles - почему C++ не теряет объекты:
Сборщик мусора периодически двигает объекты в памяти, чтобы бороться с фрагментацией. Объект переезжает на новый адрес. Для Dart-кода это не проблема - сборщик знает все ссылки и обновляет их.
Но есть код на C++ (движок, нативные библиотеки). Сборщик не знает, где у C++ лежат ссылки. Если объект переедет, C++ останется со старым адресом - программа упадет.
Решение: handles. Это ссылка на ссылку. C++ держит не объект, а handle с адресом объекта. Когда объект переезжает, сборщик обновляет адрес внутри handle. C++ всегда смотрит на актуальный адрес через handle.
🔗 Читать подробнее
💡 Вывод:
Dart управляет памятью хитро, но логично. Указатели хранят либо числа (smi), либо ссылки на объекты. Различие определяется последним битом. Сборщик следит за объектами в куче, но не трогает числа. Handles решают проблему с C++ ссылками при перемещении объектов.
На практике это значит: числа не влияют на производительность, объекты - да. Создание лишних объектов напрямую влияет на нагрузку сборщика. Понимание этих основ помогает писать более эффективный код.
Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
Post #81
749
- ❤ 6
- 👍 3
- 🙏 1