Накопление памяти после принудительного обновления - PullRequest
0 голосов
/ 23 января 2019

У меня есть приложение, подключающееся к некоторым базам данных. После первого запуска приложение занимает 300 МБ (некоторые данные загружаются из баз данных), и все в порядке. Но после нажатия кнопки «Обновить» вызывается метод обновления, при котором я переназначаю старые коллекции на новые, и проблема в том, что они после загрузки новых данных - старые данные все еще занимают память, поэтому после некоторых принудительных обновлений это может занять до 2Gb.

Я пытался заставить GC разными опциями - ничего не произошло. GC не считает, что мои коллекции должны быть полностью обновлены, а старые данные должны быть уничтожены.

Я решил создать свою собственную коллекцию Disposable, которая наследуется от List или Collection и реализует IDisposable [1]. Затем я бросил все классы, которые содержатся в таких коллекциях, и реализовал IDisposable (с деструкторами и без них).

Я использовал конструкцию () для очистки этих коллекций [2], но в результате коллекции стали пустыми, но все же заняли память.

Используя программу профилирования, я увидел, что количество объектов Gen0 увеличивается с каждым обновлением. После 10 обновлений было от 120 до 580 таких объектов, которые GC не мог очистить.

EDIT:

  1. DataContext.
  2. UnitOfWork.
  3. Repostitory.
  4. Регистрация репозитория и обслуживание репозитория.
  5. Метод в службе для получения данных.
  6. Объявление сервиса с помощью Bootstrapper и использование его для получения данных.

Итак, 8 в методе Refresh, и данные переназначаются для каждого обновления.

[1]

    public class DisposableList<T> : List<T>, IDisposable where T : 
IDisposable
{
    public DisposableList()
    {

    }

    public DisposableList(T instance)
    {
        Add(instance);
    }

    public DisposableList(IEnumerable<T> instances)
    {
        AddRange(instances);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            foreach (T item in this)
            {
                item.Dispose();
            }
        }
    }
    public void Dispose()
    {
        Dispose(true);
    }

    ~DisposableList()
    {

    }
}

[2]

using (Resources = new DisposableList<Resources>())
     {

     }

[3] * 1 034 *

public partial class MyDataContext : DbContext, IMyDataContext
{
    static MyDataContext()
    {
        Database.SetInitializer<MyDataContext>(null);
    }

    public MyDataContext(string connectionString)
        : base(connectionString)
    {
        try
        {
            if (!Database.Exists())
            {
                throw new Exception($"Нет соединения с базой данных 
                                   {Database.Connection.Database}");
            }                
        }
        catch (Exception ex)
        {
            throw ex;
        }
     }

     public IDbSet<Table> Table { get; set; }
 }

[4]

public class UnitOfWork : IUnitOfWork
     {
        DbContext _dataContext;
        IRepository[] _repositories;
        readonly object _locked = new object();

public UnitOfWork(DbContext dataContext, params IRepository[] 
                                                    repositories)
    {
        _dataContext = dataContext;
        _repositories = repositories;

    EFContextHelper.SetContext(_repositories, dataContext);
  }   


 public virtual void SaveChanges()
  {
      try
      {
          lock (_locked)
            {
                _dataContext.SaveChanges();
            }
      }
      catch (DbUpdateConcurrencyException ex)
      {
          throw;
      }
      catch (Exception ex)
      {
          throw;
      }
  }

  public virtual bool HasChanges()
    => _dataContext.ChangeTracker.HasChanges();

  public virtual void Rollback()
  {
      lock (_locked)
      {
          foreach (DbEntityEntry entry in 
                   _dataContext.ChangeTracker.Entries())
          {
  switch (entry.State)
  {
     case EntityState.Detached: break;
     case EntityState.Unchanged: break;
     case EntityState.Added:
         entry.State = EntityState.Detached;
         break;
     case EntityState.Deleted:
         entry.State = EntityState.Unchanged;
         break;
     case EntityState.Modified:
        entry.CurrentValues.SetValues(entry.OriginalValues);
        entry.State = EntityState.Unchanged;
        break;
     default:
        throw new ArgumentOutOfRangeException();
  }

          }
       }
   }

#region IDispose

    bool _disposed;
    public virtual void Dispose()
    {
        if (_disposed) { return; }

        foreach (var repo in _repositories)
        {
            repo.Dispose();
        }

        _dataContext?.Dispose();
        _dataContext = null;

        _disposed = true;
    }  
* +1039 * [5]
public interface IRepository<TEntity> : IRepository where TEntity : class
{
    TEntity Create();
    void Delete(TEntity entity);
    void Delete(IEnumerable<TEntity> entities);
    void AddOrUpdate(TEntity entity);
    void Attach(TEntity entity);
    void Reload(TEntity entity);
    void Detach(TEntity entity);
}
* * 1 042 [6]
builder.RegisterType<Service>().As<IService>();
builder.RegisterType<Repository>).As<Repository>;

[7] * +1046 *

GetData()
{    
   using (_UnitOfWorkFactory.Create(_repository))
    {
         var data = _repository.Get();
    }
}

[8] * * тысяча сорок девять

var service = Bootstrapper.Container.Resolve<IService>();
var data = service.GetData();

Итак, мое приложение должно использовать такой объем памяти, который используется после первого выполнения. Я хочу, чтобы мои старые данные были уничтожены, а новые данные заняли место старых данных.

...