Базовая модель
потребления памяти в Intl
Intl в JavaScript опирается на ICU (International
Components for Unicode) — крупную библиотеку локализации, включающую
языковые правила, таблицы форматирования и региональные данные. Основная
часть памяти, связанной с Intl, не находится в самих
объектах JavaScript, а скрыта в нативном слое движка (V8,
JavaScriptCore, SpiderMonkey).
Каждый объект Intl-форматтера
(Intl.DateTimeFormat, Intl.NumberFormat,
Intl.Collator, Intl.PluralRules,
Intl.Segmenter) представляет собой тонкую JS-обёртку над
заранее подготовленной структурой ICU. Поэтому ключевая особенность
памяти заключается в разделении:
- JS-объект — лёгкий, содержит ссылку на нативный
форматтер
- ICU-объект — тяжёлый, хранит локализованные таблицы
и правила
- Кэш движка — повторно используемые экземпляры и
данные локалей
Стоимость создания
форматтера
Создание любого форматтера является относительно дорогой операцией.
При инициализации происходит:
- разбор локали (locale resolution algorithm)
- загрузка правил форматирования из ICU
- построение внутренних структур (числовые шаблоны, календарные
данные, правила сегментации)
- инициализация опций форматирования
Память при этом выделяется не только под сам объект, но и под набор
данных локали, который может занимать значительный объём в зависимости
от языка.
Например, en, fr, de имеют
сравнительно компактные данные, тогда как локали с богатой морфологией и
сложными правилами форматирования (например, ja,
zh, ar) требуют больше ICU-структур.
Разделяемость данных и
скрытые кэши
Современные JS-движки используют агрессивное кэширование ICU-данных.
Это означает:
- повторное создание
Intl.NumberFormat('en-US') не всегда
создаёт новый набор данных
- часто используется ссылка на уже загруженные правила локали
- часть структур живёт в глобальном ICU-кэше процесса
Однако кэширование имеет ограничения:
- разные наборы опций (например,
currency,
compact, signDisplay) могут порождать новые
экземпляры
- изменение параметров форматирования ломает возможность повторного
использования
- в некоторых движках кэш ограничен по размеру и вытесняет старые
записи
Таким образом, память распределяется не линейно от числа объектов, а
от комбинаций локаль + опции.
Intl.DateTimeFormat относится к наиболее тяжёлым
форматтерам.
Причины:
- наличие календарных систем (Gregorian, Islamic, Buddhist и др.)
- таблицы названий месяцев, дней недели, периодов времени
- правила часовых поясов
- поддержка форматных шаблонов (long, short, narrow)
Дополнительно хранится информация о:
- локализации числовых систем (Latin, Arabic-Indic)
- форматах времени (12/24-hour)
- специфике отображения дат (order, separators)
При этом экземпляр форматтера может удерживать значительный объём
ICU-данных даже при отсутствии активного использования.
Intl.NumberFormat демонстрирует более вариативную модель
памяти.
Основные факторы роста памяти:
- style:
decimal, currency,
percent, unit
- notation:
standard, scientific,
engineering, compact
- currency display rules
- minimumFractionDigits / maximumFractionDigits
- locale-specific grouping rules
Особенно заметен рост памяти при использовании compact
notation, поскольку требуются таблицы сокращений чисел (K, M, B или
локальные аналоги).
Дополнительно:
- валютные правила требуют загрузки ISO-4217 справочников
- unit formatting включает таблицы единиц измерения
Intl.Collator и
лингвистические таблицы
Intl.Collator является одним из наиболее тяжёлых по
внутренним структурам компонентов.
Он включает:
- правила сортировки строк (DUCET + tailoring)
- таблицы нормализации Unicode
- алгоритмы сравнения с учётом диакритики и регистра
- локализованные исключения сортировки
Внутренне ICU хранит значительные таблицы весов символов, что делает
Collator особенно затратным по памяти при поддержке
большого числа локалей.
Особенность:
- создание множества
Collator с разными
usage и sensitivity может приводить к
увеличению количества уникальных таблиц
- частично разделяемые данные уменьшают нагрузку, но не устраняют её
полностью
Intl.PluralRules и
компактность структуры
Intl.PluralRules выглядит лёгким по сравнению с другими
форматтерами, однако:
- хранит правила множественных форм для каждой локали
- включает CLDR-таблицы категорий plural rules (one, few, many, other
и т.д.)
- зависит от типа (cardinal/ordinal)
Память растёт линейно от количества поддерживаемых категорий в
конкретной локали, но остаётся существенно ниже, чем у
DateTimeFormat или Collator.
Intl.Segmenter и
сегментационные таблицы
Intl.Segmenter (разделение текста на слова, предложения,
графемы) использует ICU Break Iterator.
Основные источники памяти:
- правила разбиения Unicode text boundaries
- языковые корректировки (особенно для CJK языков)
- state machines для анализа последовательностей символов
Наиболее тяжёлые случаи:
- японский и китайский тексты (отсутствие пробелов требует сложных
моделей сегментации)
- emoji sequences (ZWJ, grapheme clusters)
Модель жизненного цикла и
влияние GC
JS-объекты Intl управляются garbage collector’ом, но
нативные ICU-структуры могут:
- жить дольше JS-объекта из-за внутреннего кэша движка
- переиспользоваться между различными экземплярами
- освобождаться только при деградации кэша или завершении
процесса
Это создаёт асимметрию:
- удаление JS-ссылки не гарантирует мгновенного освобождения памяти
ICU
- повторное создание форматтера может не приводить к новому выделению
памяти
Различия браузеров и Node.js
Потребление памяти зависит от реализации:
V8 (Node.js, Chrome):
- агрессивное ICU-кэширование
- оптимизация повторного использования форматтеров
- единый пул локалей
JavaScriptCore (Safari):
- более консервативная модель кеширования
- частичное разделение ICU-структур
SpiderMonkey (Firefox):
- отдельные оптимизации под колляцию и сегментацию
- возможны отличия в размере кэшей
Размер ICU-данных зависит от сборки JavaScript-движка:
- full ICU build — поддержка всех локалей (десятки мегабайт)
- small ICU — ограниченный набор языков
- system ICU — использование системных библиотек локализации
При этом:
- рост числа поддерживаемых языков прямо увеличивает memory
footprint
- tree-shaking на уровне JavaScript невозможен, так как ICU — нативный
слой
Кэширование
экземпляров форматтеров в прикладном коде
Создание форматтеров внутри горячих путей приводит к избыточному
потреблению памяти из-за:
- повторной инициализации ICU структур
- увеличения количества уникальных комбинаций опций
- давления на внутренние кэши движка
С точки зрения памяти наиболее критично:
- создание
Intl.* внутри циклов
- динамическое изменение locale/options
- отсутствие повторного использования экземпляров
Переиспользование одного экземпляра:
- снижает количество ICU-аллоцированных структур
- уменьшает давление на GC
- стабилизирует memory footprint процесса
formatToParts увеличивает временные аллокации:
- создаются промежуточные структуры (массивы частей)
- увеличивается нагрузка на GC при массовом использовании
- возрастает peak memory usage
format в большинстве случаев менее затратен по памяти,
так как возвращает строку без промежуточной структуризации
результата.
Общая модель роста памяти
Память, связанная с Intl, можно представить как
сумму:
- базовый ICU runtime footprint
- кэш локалей
- уникальные комбинации format options
- активные экземпляры форматтеров
- временные структуры форматирования
Ключевая особенность — нелинейный рост при увеличении разнообразия
локалей и опций, а не количества вызовов форматирования.