Flyweight (Приспособленец): экономия памяти за счёт разделяемого состояния
Flyweight позволяет хранить миллионы однотипных объектов, разделяя их общее состояние, которое не нужно дублировать. Внутреннее состояние объекта кэшируется и переиспользуется многими экземплярами, а внешнее состояние передается при вызове.
Паттерн часто применяется в highload, графических подсистемах, кэшировании, обработке больших массивов данных, игровых движках, аналитике и оптимизации работы ORM.
Flyweight значительно уменьшает расход памяти и ускоряет работу, когда количество объектов огромно, а их общие данные можно вынести в разделяемый пул.
Flyweight — паттерн, который редко упоминается новичками, но встречается в настоящей архитектуре гораздо чаще, чем кажется. Он решает проблему, возникающую в системах, где нужно создать:
-
миллионы однотипных сущностей;
-
миллионы записей аналитики;
-
миллионы графических объектов;
-
миллионы элементов в структурах данных;
-
огромные структуры кэша;
-
словари, каталоги, справочники;
-
повторяющиеся элементы UI;
-
большие графы (social graph, product graph).
В таких системах простое создание объектов приводит к переполнению памяти. Flyweight разделяет состояние объекта на:
-
внутреннее (intrinsic) — одинаковое для всех, хранится в пуле;
-
внешнее (extrinsic) — уникальное, передается при использовании.
Объект не хранит данные, которые можно вынести наружу.
Как работает Flyweight
1. Создается пул (кэш) разделяемых объектов
Это могут быть:
-
строки,
-
параметры конфигурации,
-
неизменяемые структуры,
-
кэш шаблонов,
-
заготовки сущностей.
2. Клиент запрашивает объект
Если он есть в пуле — возвращается существующий.
Если нет — создаётся, добавляется в пул и переиспользуется.
3. Уникальные параметры передаются при вызове
То есть объект не хранит состояние, которое ему не нужно постоянно.
Где используется Flyweight в архитектуре
1. ORM и большие модели данных
Например, в PostgreSQL, Hibernate, EF Core хранятся:
-
кэш схемы БД,
-
метаданные столбцов,
-
типы,
-
маппинги.
Они не создаются заново для каждой сущности.
2. Каталоги и справочники
Один и тот же товар может использоваться в:
-
истории заказов,
-
аналитике,
-
корзинах,
-
рекомендациях,
-
промо.
Но описание товара одинаковое → это Flyweight.
3. Игровые движки
Unity/Unreal используют Flyweight для:
-
текстур,
-
моделей,
-
шейдеров,
-
анимаций.
Память экономится в десятки раз.
4. Файловые системы
Файловые дескрипторы имеют разделяемые метаданные.
5. Аналитика и BigData
Огромные структуры данных используют Flyweight для:
-
кодирования строк (dictionary encoding),
-
хранения типов,
-
колонок в столбцовых БД.
Apache Parquet, ORC, Arrow — все это используют Flyweight-подход.
6. Графические интерфейсы
UI-элементы, шрифты, стили, шаблоны — разделяемые объекты.
7. Интернирование строк (String Interning)
Java, .NET, Python активно используют flyweight для строк.
Одна строка — много ссылок.
8. Хранилища кэшей
В Redis, Memcached, локальных LRU-кэшах объекты разделяются между всеми клиентами.
Преимущества Flyweight
-
гигантская экономия памяти;
-
возможность работать с миллионами объектов;
-
повышение скорости;
-
уменьшение GC нагрузки;
-
оптимизация highload-архитектур;
-
повышение стабильности системы под нагрузкой.
Недостатки
-
сложность разделения состояния;
-
рост числа параметров при вызовах;
-
необходимость аккуратной модели неизменяемости;
-
опасность утечек памяти при неправильном пуле.
Но для больших нагрузок Flyweight — спасение.
Flyweight в российских учебниках
Подробно разбирается в:
-
Замотайлова;
-
Беляев;
-
Орлова;
-
Лобанов;
-
Астапчук;
-
Рябченко.
Обычно — в разделах:
-
оптимизация производительности;
-
архитектура больших систем;
-
структуры данных;
-
проектирование ИС высокого масштаба.
Почему архитектор обязан знать Flyweight
Потому что в реальных системах объекты:
-
повторяются;
-
дублируют состояние;
-
занимают огромные объёмы памяти;
-
нагружают GC;
-
препятствуют масштабированию.
Flyweight позволяет строить архитектуру, которая:
-
выдерживает большие нагрузки;
-
не падает от GC stop-the-world;
-
использует память эффективно;
-
работает быстро и стабильно.
Это незаменимый паттерн для больших ИС, DWH, аналитики, highload и графовых моделей.