У меня есть приложение, подключающееся к некоторым базам данных. После первого запуска приложение занимает 300 МБ (некоторые данные загружаются из баз данных), и все в порядке. Но после нажатия кнопки «Обновить» вызывается метод обновления, при котором я переназначаю старые коллекции на новые, и проблема в том, что они после загрузки новых данных - старые данные все еще занимают память, поэтому после некоторых принудительных обновлений это может занять до 2Gb.
Я пытался заставить GC разными опциями - ничего не произошло. GC не считает, что мои коллекции должны быть полностью обновлены, а старые данные должны быть уничтожены.
Я решил создать свою собственную коллекцию Disposable, которая наследуется от List или Collection и реализует IDisposable [1]. Затем я бросил все классы, которые содержатся в таких коллекциях, и реализовал IDisposable (с деструкторами и без них).
Я использовал конструкцию () для очистки этих коллекций [2], но в результате коллекции стали пустыми, но все же заняли память.
Используя программу профилирования, я увидел, что количество объектов Gen0 увеличивается с каждым обновлением. После 10 обновлений было от 120 до 580 таких объектов, которые GC не мог очистить.
EDIT:
- DataContext.
- UnitOfWork.
- Repostitory.
- Регистрация репозитория и обслуживание репозитория.
- Метод в службе для получения данных.
- Объявление сервиса с помощью 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();
Итак, мое приложение должно использовать такой объем памяти, который используется после первого выполнения. Я хочу, чтобы мои старые данные были уничтожены, а новые данные заняли место старых данных.