Компиляторы

Управление памятью вручную

70% всех CVE в Chrome, Android и Windows связаны с ошибками управления памятью: use-after-free, buffer overflow, double-free. Не потому что разработчики неопытные - а потому что C/C++ не дают инструментов для статической проверки. Rust borrow checker делает эти классы ошибок невозможными на этапе компиляции. Это объясняет, почему Microsoft, Google и Android переписывают системный код на Rust.

  • **jemalloc** в Rust std и Firefox: снижение фрагментации heap на 20-40% по сравнению с glibc malloc, особенно заметно в долгоживущих процессах
  • **LLVM BumpPtrAllocator**: Clang аллоцирует ~90% AST nodes через bump allocator - это одна из причин быстрого времени компиляции Clang по сравнению с GCC
  • **Android 13+**: ~1.5M строк Rust в AOSP, включая Bluetooth stack и Keystore2 - с 2021 года zero CVE по memory safety в Rust-компонентах

malloc/free и аллокаторы

malloc (memory allocator) управляет heap через списки свободных блоков. glibc malloc (ptmalloc2) использует bins: small bins для блоков 16-504B, large bins для больших, unsorted bin как буфер. При free() блок возвращается в bin и по возможности сливается с соседними. Каждый блок имеет 8-16 байт metadata overhead.

Heap fragmentation - практическая проблема: после миллионов аллокаций/деаллокаций свободная память фрагментирована. Redis решает это через zmalloc: объекты одного размера хранятся в слабах, нет фрагментации внутри slab. Nginx использует pool аллокатор per-request - всё освобождается разом после ответа.

Почему tcmalloc быстрее glibc malloc для многопоточного кода?

Arena Allocators

Arena (bump allocator, region allocator) - аллокация из непрерывного буфера через инкремент указателя. Освобождение - одна операция (сброс указателя или drop всей арены). Нет фрагментации, аллокация straightforwardly быстрая. Используется везде, где lifetime объектов совпадает: AST компилятора, JSON-парсинг, request в веб-сервере.

Rust bumpalo crate - production-grade bump allocator. Go compiler использует arena аллокаторы для IR nodes. LLVM BumpPtrAllocator выделяет память чанками и переключается на следующий чанк при переполнении. Ключевое ограничение: нельзя освободить отдельный объект - только всю арену.

Почему arena allocator недостаточен для общего случая (general-purpose)?

RAII: Resource Acquisition Is Initialization

RAII (C++, Bjarne Stroustrup) - ресурс привязан к времени жизни объекта. Деструктор вызывается автоматически при выходе из scope. Никаких утечек при исключениях. std::unique_ptr - RAII ownership, std::shared_ptr - RAII с reference counting, std::lock_guard - RAII mutex.

RAII полностью решает resource leak проблему для C++ при дисциплинированном использовании. Clang Tidy (cppcoreguidelines-*) статически проверяет использование raw pointers вместо RAII. Chrome codebase имеет scoped_refptr - собственные RAII wrappers с дополнительными проверками для безопасности браузера.

Что гарантирует RAII при возникновении исключения внутри функции?

Rust Borrow Checker

Rust borrow checker - compile-time анализ ownership и lifetimes. Правила: каждое значение имеет одного владельца; при передаче ownership перемещается (move); можно создать любое количество shared references (&T) или одну mutable reference (&mut T) - но не оба одновременно. Нарушение правил - ошибка компиляции, не runtime.

Android Open Source Project (AOSP) переводит C/C++ компоненты на Rust с 2021 года: Android 13+ имеет ~1.5M строк Rust. Цель - устранить 70% CVE связанных с memory safety (UAF, buffer overflow). Microsoft Azure переписывает части Windows Kernel на Rust. Chromium начал добавлять Rust компоненты в 2023 году.

Rust borrow checker делает любую программу автоматически быстрее за счёт отсутствия GC

Rust исключает GC-паузы, но программист платит временем на борьбу с borrow checker и иногда вынужден клонировать данные там, где GC-язык поделился бы ссылкой

Для некоторых алгоритмов (граф с циклами, двусвязный список) Rust требует unsafe код или Rc/RefCell, что нивелирует часть преимуществ. Rustonomicon документирует паттерны где безопасный Rust невозможен без unsafe

Почему Rust запрещает иметь &mut T и &T на один объект одновременно?

Итоги

  • malloc/free с thread-local caches (tcmalloc, jemalloc): аллокация < 50ns, но фрагментация и overhead metadata ~16 bytes/block
  • Arena allocators: bump pointer аллокация за 1-3 инструкции, O(1) освобождение всех объектов - идеально для AST, request-scope объектов
  • Rust borrow checker: статическая гарантия memory safety без GC; XOR mutability исключает use-after-free и data races на этапе компиляции

Связанные темы

Управление памятью - основа для понимания GC и производительности компиляторов:

  • Основы GC — GC автоматизирует то, что malloc/free делает вручную; понимание аллокаторов объясняет overhead GC
  • Продвинутый GC — ZGC и Shenandoah - альтернатива ручному управлению памятью для managed языков
  • Спекулятивные оптимизации — Escape analysis (JIT) определяет, можно ли аллоцировать объект на стеке вместо heap

Вопросы для размышления

  • Rust borrow checker иногда требует клонировать данные там где Java использует shared reference. В каких структурах данных borrow checker создаёт наибольшие трудности?
  • Arena аллокаторы требуют что все объекты имеют одинаковый lifetime. Как компиляторы (Clang, rustc) организуют арены, если AST узлы имеют разные lifetimes?
  • Android переписывает C++ на Rust для memory safety. Какой overhead (performance, code size, developer productivity) это создаёт и как Google его измеряет?

Связанные уроки

  • os-07-memory
Управление памятью вручную

0

1

Войти