Как уменьшить потребление памяти во время постоянного обновления дерева?

Рейтинг: 0Ответов: 0Опубликовано: 02.02.2023

У меня есть класс TreeNode, который отвечает за ноду, выглядит вот так:

public class TreeNode                                                                                 
    : ViewModelBase                                                                                   
{                                                                                                     
    protected TreeNode(string part)                                                                 
        : this( part, -1, null)                                                                      
    {                                                                                                 
                                                                                                      
    }                                                                                                 
                                                                                                      
    private TreeNode(                                                                                 
        string part,                                                                                  
        int level,                                                                                    
        TreeNode? parent)                                                                            
    {                                                                                                 
        Part = part;                                                                                  
        Level = level;                                                                                
        Parent = parent;                                                                              
        Root = parent?.Root ?? this;                                                                  
                                                                                                      
        if ( parent != null )                                                                         
            MessagesCount = 1;                                                                        
    }                                                                                                 
                                                                                                      
    #region IO                                                                                        
                                                                                                      
    private bool _updateEnqueued;                                                                     
    public int Level { get; }                                                                         
    public TreeNode? Parent { get; }                                                                  
    public TreeNode Root { get; }                                                                     
                                                                       
    private bool _isExpanded;                                                                         
    public bool IsExpanded                                                                            
    {                                                                                                 
        get => _isExpanded;                                                                           
        set                                                                                           
        {                                                                                             
            this.RaiseAndSetIfChanged(ref _isExpanded, value);                                      
            Root.EnqueueUpdate();                                                                     
        }                                                                                             
    }                                                                                                 
                                                                                                      
    private AvaloniaList<TreeNode> _visibleChildren = new();                                          
    public IAvaloniaReadOnlyList<TreeNode> VisibleChildren                                            
        => _visibleChildren;                                                                          
                                                                                                        
    public readonly Dictionary<string, TreeNode> _treeChildren = new();
    public ObservableCollection<ReceivedHistoryMessage> UiHistoryMessages { get; set; } = new();

                           
                                                                                                      
    #endregion                                                                                        
                                                                                                      
    public bool Add(                                                                                  
        string[] subTopic,                                                                            
        MqttDelivery delivery)                                                                       
        => Add(subTopic, Level + 1, delivery);                                                      
                                                                                                      
    public void ForceResync()                                                                         
        => Root.Update();                                                                             
                                                                                                                                                                                                                                                                                                    
    private bool Add(                                                                                 
        string[] topic,                                                                               
        int level,                                                                                    
        MqttDelivery delivery )                                                                       
    { 
        if ( level >= topic.Length )                                
        {                                                           
           Topic = delivery.Topic;                                 
           Header = delivery.Header?.ToJsonString( true );         
           Payload = delivery.Payload?.ToJsonString( true );       
           Delivery = delivery;                                    
                                                        
           AddToHistory( delivery );                               
           return false;                                           
        }
                                                                                                                                                                                                                                                                                                                                 
        var part = topic[level];                                                                    
                                                                                                      
        if (!_treeChildren.ContainsKey(part))                                                     
        {                                                                                             
            TopicsCount++;                                                                            
            _treeChildren.Add(part, new TreeNode(part, level, this));                             
            Root.EnqueueUpdate(); // обновление ноды                                                                    
        }                                                                                             
                                                                                                      
        MessagesCount++;                                                                              
                                                                                              
        _treeChildren[ part ].Add(topic, level + 1, delivery);                                      
                                                                                                      
        return true;                                                                                  
    }                                                                                                 
                                                                                                      
    private void EnqueueUpdate()                                                                      
    {                                                                                                 
        if (_updateEnqueued)                                                                        
            return;                                                                                   
                                                                                                      
        _updateEnqueued = true;                                                                       
                                                                                                      
        Dispatcher.UIThread.Post(                                                                     
            Update,                                                                                   
            DispatcherPriority.Background);                                                          
    }                                                                                                 
                                                                                                      
    private void AppendItems()                                                                        
    {                                                                                                 
        _updateEnqueued = false;                                                                      
                                                                                                      
        var flatTreeList = new AvaloniaList<TreeNode>();                                              
                                                                                                      
        AppendItems(flatTreeList, this);                                                            
                                                                                                      
        _visibleChildren = flatTreeList;                                                              
    }                                                                                                 
                                                                                                      
    private void AppendItems(                                                                         
        AvaloniaList<TreeNode> flatTreeList,                                                          
        TreeNode treeNode )                                                                           
    {                                                                                                 
        flatTreeList.Add(treeNode);                                                                 
                                                                                                      
        if (!treeNode.IsExpanded)                                                               
            return;                                                                                   
                                                                                                      
        foreach (var ch in treeNode._treeChildren)                                                  
            AppendItems(flatTreeList, ch.Value);                                                    
    }                                                                                                 
                                                                                                      
    private void Update()                                                                             
    {                                                                                                 
        AppendItems();                                                                                
        Root.RaisePropertyChanged(nameof(Root.VisibleChildren));                                  
    }

    private void AddToHistory( MqttDelivery mqttMessage )                                       
    {                                                                                           
         while ( UiHistoryMessages.Count >= 30 )                                                 
             UiHistoryMessages.RemoveAt( UiHistoryMessages.Count - 1 );                          
                                                                                        
         UiHistoryMessages.Insert( 0, new ReceivedHistoryMessage                                 
         {                                                                                       
            Id = _historyId++,                                                                  
            Time = DateTime.Now,                                                                
            Message = mqttMessage,                                                              
            UIFormattedMessage = $"{DateTime.Now:hh:mm:ss}, 
             {mqttMessage.Header.ToJsonString()}"
         } );                                                                                    
    }                                                                                           
                                                                                                                                                                                                                                                                                            
}

                                 

Раньше я обновлял дерево по клику на элемент дерева - нажал, раскрылось, обновилось (set в свойстве IsExpanded) и все хорошо, но появилось предложение обновлять постоянно, т.к. можно не нажать и не увидеть чего-то нового. Я добавил обновление еще и в метод Add, но появилась проблема - память слишком быстро тратится и я не знаю, что с этим можно сделать. Расстраивает то, что память не контролируется - полный день работы приложения и минус пару гб оперативной памяти, а хотелось бы поддерживать какое-то постоянное значение, желательно в рамках 150-200 мб, что и было раньше, до добавления обновления ноды в метод Add. Подскажите, пожалуйста, как лучше сделать? Как бы вы сделали?

upd:

У меня есть класс ConnectionLogic, в котором объявлено вот такое свойство: public TreeRoot TreeRoot { get; }

Логика следующая: мне шина присылает сообщения, на каждое сообщение я вызываю метод Add и выглядит это так:

var subTopic = mqttDelivery.Topic.Split('/');
TreeRoot.Add(
   subTopic: subTopic,
   delivery: mqttDelivery); 

Что происходит в методе: в словарь добавляются элементы, они же ветви дерева, одновременно все это рисуется на ui. Будем считать, что ui - это wpf, где есть биндинги. Поэтому на UI показываются элементы коллекции VisibleChildren. А добавляются элементы в эту коллекцию методом AppendItems, в котором происходит проход по словарю (ранее используется в методе Add) Обновление же коллекции вызывает метод Update, в котором и происходят аллокации (обратите внимание на Root.RaisePropertyChanged(...))

Проблема прям на поверхности: расходуется много памяти, а главное - это не контролируется. Клик по дереву вызывает метод Update, добавление в дерево вызывает метод Update. Все это и приводит к утечкам. У меня такое чувство, будто память вообще не очищается - никогда не замечал, чтобы уменьшалось потребляемое кол-во, только с каждым кликом и добавлением в дерево увеличивается и увеличивается потребление

Ответы

Ответов пока нет.