Почему Blazor Server потребляет всё больше памяти при обновлении страницы и не освобождает её?
Самообучаюсь Blazor уже пол года и периодически ловлю себя на мысли, что не понимаю как работает Blazor.
У меня есть интерактивные формы для работы с БД и все это работает, но сейчас я столкнулся с тем, что простое обновление страницы пользователем приводит к неконтролированному выделению памяти на любых страницах, начиная от страниц "counter" из шаблона по умолчанию, где каждое обновление страницы прибавляет 3-5мб и заканчивая моими сложными страницами, где после десятикратного обновления страницы расходуется 10гб памяти(при том, что первая загрузка страницы занимает всего 10-50мб памяти).
При этом, эта память не освобождается после того, как пользователь переходит на другую страницу или даже закрывает все свои страницы(может пройти 10 минут после того, как пользователь закрыл все вкладки и память при этом все еще остается занятой).
Да, я в курсе про предварительный рендеринг и понимаю как с ним работать.
Я догадываюсь, что философия Blazor состоит в том, что для улучшения производительности рендеринга страниц нужно держать в памяти состояния компонентов, но цена за это слишком высока и не понятно, как этим управлять.
У меня был опыт написания проектов на MVC и там философия была проста и понятна - используй память для вычислений и отрисовки, выдай результат в браузер и очисти память.
Я читал различные багрепорты про утечки памяти в Blazor и различное потребление памяти в режиме отладки и без нее, но так и не нашел какого-то внятного объяснения, почему простое обновление странички хелловорда из готового шаблона Blazor отъедает память и не освобождает ее.
Может-ли кто-то поделиться своим пониманием этой проблемы?
Ответы (1 шт):
Документация отмечает, что память не освобождается мгновенно, поскольку Circuit сохраняется на сервере в течение 3 минут. Такое поведение является ожидаемым для Blazor Server и отражено в официальных материалах.
Когда клиент отключается, Blazor Server сохраняет состояние circuit для возможности восстановить соединение.
Blazor Server — это stateful-платформа
Каждое открытие страницы создаёт новый Circuit, который хранит состояние компонентов, DI-scope и активное соединение SignalR.
Это позволяет платформе обеспечивать интерактивность без JS-фреймворков.
Поэтому рост памяти на несколько мегабайт при обновлении страницы — нормальное поведение. Это стоимость поддержания состояния UI на сервере.
Good practices - Всегда отписывайтесь от событий и Dispose-айте объекты
@implements IDisposable
@code {
public void Dispose()
{
service.OnChanged -= HandleChange;
timer?.Dispose();
cts?.Dispose();
}
}
Timer=> нужно вызыватьDispose()Taskс бесконечным циклом => нуженCancellationTokenCTS=> обязательноDispose()
но 10Гб от простого обновления страницы это много
Как отслеживать жизненный цикл компонентов:
Добавьте в appsettings.json:
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.Components.Server.Circuits": "Debug"
}
}
Blazor начнёт логировать => смотрите в VS в консоли вывода
- создание/уничтожение Circuit
- Dispose компонентов
- реконнекты
- ошибки жизненного цикла
Это лучший способ проверить, освобождаются ли компоненты.
Если нужна модель MVC ("ничего не хранить на сервере")
Используйте Blazor WebAssembly — там нет Circuit, нет SignalR-сессии и нет серверного состояния UI.