Управление памятью является одним из ключевых аспектов производительности и стабильности современных языков программирования. CRuby, официальная реализация языка Ruby, известна своей простотой и эффективностью, но при этом сталкивается с рядом вызовов, связанных с современными требованиями к эффективности работы и масштабируемости. Современные разработки в области переработки управления памятью в CRuby, в частности интеграция Memory Management Toolkit (MMTk), открывают новые горизонты для улучшения производительности и адаптации языка к высоким нагрузкам. Партнерство между Австралийским национальным университетом (ANU) и компанией Shopify направлено на создание нового поколения сборщика мусора для Ruby с использованием MMTk - модульного и платформонезависимого набора инструментов для быстрой разработки высокопроизводительных сборщиков мусора. Доминирующий в CRuby алгоритм Mark and Sweep уступает место более сложным и продвинутым методам, таким как Generational Immix и Sticky Immix, которые могут значительно повысить эффективность управления памятью.
На данный момент существует две реализации MMTk для Ruby. Первая - это форк Ruby, поддерживаемый самой командой MMTk и обладающий примерно пятилетним опытом развития и экспериментов. Вторая - интеграция в основной код Ruby с использованием модульной архитектуры сборщика мусора. Хотя вторая реализация менее зрелая, она играет важную роль в формировании экосистемы сборщиков мусора для Ruby. Одной из ключевых проблем при внедрении MMTk в CRuby стала обработка так называемых объектных ссылок, адреса которых могут изменяться при перемещении объектов в памяти.
В Ruby 2.7 впервые появился движущийся сборщик мусора, который потребовал реализации механизма pinning - фиксации объектов, которые нельзя перемещать. Это усложнило совместимость и потребовало дополнительной логики для обновления адресов объектов во всех типах данных. Современные алгоритмы MMTk, напротив, обычно перемещают объект сразу после его маркировки, объединяя этапы маркировки и сжатия. Такая модель требует заранее знать, какие объекты должны быть закреплены, что невозможно без повторного сканирования всего кучи, что было бы неэффективно.
Разработчики предложили концепцию "Потенциально закрепляющих родителей" (Potentially Pinning Parents, PPP) - объектов, которые могут содержать ссылки на некондиционные к перемещению объекты. За последние годы количество PPP объектов было существенно уменьшено, и большинство пользоватльских объектов Ruby перестали ими быть, что значительно упростило процесс сборки мусора и повысило его производительность. Немалая сложность связана с финализацией - процессом освобождения ресурсов и выполнением специального кода при уничтожении объектов. В Ruby до версии 3.2 все объекты выделялись в слотах фиксированного размера, а дополнительные ресурсы - посредством вызовов malloc, что накладывало ограничения и осложняло управление памятью.
Внедрение переменной ширины выделения памяти позволило создавать объекты размером, соответствующим их данным, однако полное избавление от malloc пока невозможно. Одним из важных преимуществ MMTk является поддержка параллельного выполнения сборки мусора, что позволяет разбить работу на мелкие задачи (work packets) и выполнять их на нескольких ядрах процессора, значительно ускоряя сборку и делая ее более масштабируемой. При этом MMTk не поддерживает одновременное выполнение с кодом программы - остановка виртуальной машины Ruby все еще требуется. Переход от последовательного к параллельному сборщику потребовал решения проблем с локальными потоковыми переменными и условиями гонки. Несмотря на высокие ожидания, разработчики столкнулись с неожиданным эффектом: использование большего числа потоков приводило к замедлению финализации.
Анализ производительности показал, что функция free, освобождающая память, выделенную через malloc, плохо масштабируется в многопоточной среде из-за реализации большинства современных аллокаторов, таких как glibc, jemalloc и tcmalloc. Исключением стал mimalloc, демонстрирующий относительно лучшую производительность, но и у него почти отсутствует прирост при использовании более четырех потоков. Для решения этой проблемы было принято отказаться от malloc в пользу выделения буферов для наиболее часто используемых типов объектов Ruby (Array, String, MatchData) в виде скрытых Ruby объектов, управляемых самим сборщиком мусора. Это позволяет интегрировать управление памятью различных компонентов Ruby в общую систему, автоматизировать очистку и снизить накладные расходы на финализацию. В целом интеграция MMTk в CRuby открывает широкие возможности для оптимизации и дальнейшего развития Ruby как языка для масштабируемых и производительных приложений.
Исследования в этом направлении продолжаются, включая оптимизацию макета памяти, улучшение методов перемещения объектов и интеграцию с JIT-компиляторами. Сотрудничество исследователей ANU и инженеров Shopify демонстрирует, что совместное развитие открытых проектов и академических инициатив может существенно продвинуть вперед экосистему Ruby, сделав её более современной и конкурентоспособной. Полученный опыт помогает не только создавать новые инструменты, но и информирует о необходимых изменениях в ядре Ruby, способствуя улучшению языка в целом. Переосмысление подходов к управлению памятью в CRuby с привлечением современных средств и идей является важным шагом к созданию "языка на сто лет", способного эффективно справляться с текущими и будущими вызовами мирa разработки программного обеспечения. Благодаря инновациям и беспрерывному экспериментальному процессу Ruby сохраняет свою актуальность и привлекательность для разработчиков по всему миру.
.