Blazor: обновление с использованием InvokeAsync вызывает утечку памяти

#blazor

#blazor

Вопрос:

У меня есть приложение Blazor со списком, который я хочу обновлять автоматически с помощью таймера каждые X секунд. Когда я запускаю приложение, используя следующий код, список обновляется каждые 10 секунд, но использование памяти увеличивается при каждом обновлении. Использование памяти приложением начинается с 2-300 МБ и превышает 1 ГБ в течение 10 обновлений.

Я надеялся, что мой код будет перезаписывать существующий список при каждом обновлении, таким образом, не используя все больше и больше памяти. Чего мне не хватает? Может быть, это связано с тем, что обновление выполняется в нескольких потоках?

 public partial class MyBlazorComponent
{
    private Dictionary<Guid, IList<MyModel>> MyList { get; set; }
    private Timer Timer { get; set; }
    
    // A simple lock to make sure that multiple refreshes don't overlap
    private bool _isRefreshing;

    protected override async Task OnInitializedAsync()
    {
        await UpdateMyList(); // the initial loading of the list
        await base.OnInitializedAsync();

        Timer = new Timer(async _ =>
        {
            await RefreshList();
        }, null, 0, 10000);
    }

    private async Task UpdateMyList()
    {
        _isRefreshing = true;
        MyList = null;
        MyList = await _myService.GetList(); // external service to get list contents
        _isRefreshing = false;
    }
    
    private async Task RefreshList()
    {
        if (!_isRefreshing)
        {
            // Without these two lines, the list won't update
            MyList = null; 
            await InvokeAsync(StateHasChanged);
            
            // Update the list
            await UpdateMyList();
            await InvokeAsync(StateHasChanged);
        }
    }
}
 

Комментарии:

1. Я сделал небольшую версию кода, которым вы поделились ( blazorfiddle.com/s/sr0spgs8 ), но я не сталкиваюсь с проблемами, которые вы описываете. Когда я запускаю его локально, я также не замечаю утечки памяти. Также следует отметить, что мне не нужно дважды вызывать StateHasChanged для изменения списка. Не могли бы вы поделиться некоторыми кодами razor и, возможно, более подробной информацией о службе? Мне не кажется, что проблема в предоставленном вами коде.

2. Почему вы используете StateHasChanged гораздо меньше InvokeAsync(StateHasChanged) ? Замена списка приведет к перерисовке только затронутых элементов. StateHasChanged вызовет обновление всей страницы. InvokeAsync …. какой в этом смысл? Вы пробовали просто вызывать UpdateMyList таймер?

3. Кстати, какую версию Blazor вы используете? Серверная или веб-сборка? Требуются старые версии Blazor StateHasChanged , .NET 5 не работает

4. Примечание стороны, вы пропускаете таймер, но это не приведет к использованию памяти, которое вы видите.

5. Полностью пропустил это @HenkHolterman. Это действительно должно реализовать IDispose, чтобы избавиться от таймера, когда он больше не используется.

Ответ №1:

Похоже, проблема здесь не в предоставленном коде, а в инструкции _myService.getList() . Как четко заметил @HenkHolterman, единственная утечка памяти в этом коде заключается в том, что таймер не удаляется, когда компонент больше не используется. Это можно сделать следующим образом:

 public partial class MyBlazorComponent : IDisposable
{
    private Timer Timer { get; set; }

    //other code.....

    public void Dispose()
    {
        Timer?.Dispose();
    }
}