Не удалось освободить фрейм-память Entity
Я использую очень простое приложение asp.net mvc с Entity Framework 6.0.2,.Net 4.5.1:
public class HomeController : Controller
{
public ActionResult Index()
{
int count;
using (var db = new LocalContext())
{
count = db.Counters.Count();
}
return View(count);
}
}
public class Counter
{
public int Id { get; set; }
}
public class LocalContext : DbContext
{
public DbSet<Counter> Counters { get; set; }
}
Если я выполняю нагрузку на нем, я в конечном итоге получаю исключение из памяти. (tinyget -srv:localhost -port:<port> -uri:/home/index/ -threads:30 -loop:5000
). В мониторе производительности я вижу, что поколение 2 Heap постоянно растет. Если я использую меньшее значение цикла (скажем, 500), размер увеличивается до тех пор, пока tinyget не остановится. Затем размер кучи остается неизменным (по крайней мере 20 минут, после чего я остановил сервер).
Что я делаю неправильно?
ИЗМЕНИТЬ
Итак, я попробовал предложение Саймона Моурье и отказался от кода EF. Тогда у меня нет проблем с памятью. Поэтому я подумал, может быть, если я использую Release вместо Debug, это будет иметь значение. И это было! Через некоторое время память была выпущена, и я мог разместить большую нагрузку на сайте. Затем я переключился обратно в Debug, чтобы узнать, могу ли я получить больше информации и... даже в режиме отладки больше нет проблем. FML, я работал над этим днем, и теперь я больше не могу воспроизвести его.
Ответы
Ответ 1
В вашем случае внутренне управляемый класс, который наследует от DbContext, должен затем реализовать IDisposable и внутри LocalContext добавить следующее:
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// Manage any native resources.
}
//Handle any other cleanup.
}
Без особого переопределения вызова для распоряжения оператор using будет только вызывать Dispose() для базового класса, тогда как вам нужно избавиться от родительского и базового.
Ответ 2
Это может быть не правильный ответ, но я предлагаю сохранить ваш контекст, управляемый контейнером IoC. И добавьте его с областью TrasientScope или PerHttpRequest (пример не указан из-за большой разновидности синтаксиса контейнера ioc). Если вам нужен конкретный пример, ответьте, для чего вы хотите
Ответ 3
Собственно, OutOfMemotyException
является нормальным в этой ситуации, поскольку сборщик мусора не возникает сразу после того, как вы закончили с объектом. В этом случае вам нужно использовать GC.Collect() для выполнения коллекции во всех поколениях памяти и немедленно вернуть всю незащищенную память.
public class HomeController : Controller
{
public ActionResult Index()
{
int count;
using (var db = new LocalContext())
{
count = db.Counters.Count();
}
GC.Collect();
return View(count);
}
}
Обратите внимание, что вы не должны использовать GC.Collect() в производственном коде, так как он мешает механизму сбора мусора.
Ответ 4
Я хотел бы создать соединение класса с БД..
public class DBconnection : IDisposable
{
private ChatEntities _db = new ChatEntities();
protected ChatEntities Db {
get { return _db; }
}
public void Dispose()
{
if (_db != null)
{
_db.Dispose();
}
}
}
Затем, когда вы хотите подключиться и манипулировать.. Позволяет называть его классом DBlogic..
public class DBlogic : DBconnection
{
internal void WriteToDB(String str){
//Do something ...
Db.SaveChanges();
}
}
Это приведет к тому, что Dispose опустит ресурсы.. плюс его очиститель.. по крайней мере для моих глаз: D
Ответ 5
Я не вижу ничего плохого в вашем коде. Возможно, это может быть проблемой с базовым поставщиком ADO.NET. Какую базу данных вы используете?
Я помню, что у меня были проблемы с некоторыми unit test, которые не выпускали файлы базы данных SQLite, которые я в конечном итоге решил с помощью этого кода (в моем классе DbContext)
public class LocalContext : DbContext
{
protected override void Dispose(bool disposing)
{
var connection = this.Database.Connection;
base.Dispose(disposing);
connection.Dispose();
}
}
Может быть несвязанным, но я бы попробовал.