Некоторая предыстория:
Работа с:
- .NET 4.5 (мышление перехода на 4.5.1, если оно безболезненно)
- Веб-формы
- Entity Framework 5, Lazy Loading enabled
- Контекст на запрос
- IIS 8
- Datacenter Windows 2012
Опасность: использование памяти
В рамках проекта, который мы сейчас проводим, и, возможно, нашего первого крупного проекта, мы часто читаем некоторые более крупные фрагменты данных, поступающие из импорта CSV, которые, вероятно, останутся неизменными в течение очень длительных периодов времени.
Если кто-то явно не импортирует CSV-данные, они гарантированно будут такими же, это происходит в более чем одном месте в нашем проекте, и аналогичный подход используется для некоторых обычных документов, которые часто читаются пользователями. Мы решили кэшировать эти данные в кеше HttpRuntime.
Это происходит так, и мы тянем около 15 000 записей, состоящих в основном из строк.
//myObject and related methods are placeholders
public static List<myObject> GetMyCachedObjects()
{
if (CacheManager.Exists(KeyConstants.keyConstantForMyObject))
{
return CacheManager.Get(KeyConstants.keyConstantForMyObject) as List<myObject>;
}
else
{
List<myObject> myObjectList = framework.objectProvider.GetMyObjects();
CacheManager.Add(KeyConstants.keyConstantForMyObject, myObjectList, true, 5000);
return myObjectList;
}
}
Получение данных для вышеуказанного метода очень просто и выглядит следующим образом:
public List<myObject> GetMyObjects()
{
return context.myObjectsTable.AsNoTracking().ToList();
}
Вероятно, есть вещи, которые нужно сказать о структуре кода, но это не моя проблема в настоящий момент.
Я начал профилировать наш проект, как только увидел высокую память и нашел много частей, где наш код мог быть оптимизирован. Я никогда не сталкивался с 300 одновременными пользователями, и наши внутренние тесты, сделанные самими, были недостаточными, чтобы показать проблемы с памятью. Я выделил и исправил многочисленные утечки памяти, но я хотел бы понять некоторые неизвестные Entity Framework.
Учитывая приведенный выше пример и используя ANTS Profiler, я заметил, что " myObject" и другие подобные объекты ссылаются на многие System.Data.Entity.DynamicProxies.myObject, кроме того, существует много EntityKeys, которые сохраняются для целых чисел. Они не принимают много, но их количество относительно велико.
Например, 124 экземпляра " myObject" ссылаются почти на 300 System.Data.Entity.DynamicProxies.
Обычно он выглядит так, независимо от объекта: Некоторая запись в кэш, некоторый объект, который я кэшировал, и теперь я заметил, что многие из них были отделены от предыдущего кэширования dbContext, динамических прокси и objectContext. Я не знаю, как развязать их.
Мой прогресс:
Я провел некоторое исследование и выяснил, что я могу кэшировать что-то, связанное с объектами Framework Entity Framework. Я вытащил их с помощью NoTracking, но в памяти все еще есть те DynamicProxies, которые, вероятно, увлекаются и другими вещами.
Важно: Я наблюдал некоторые живые экземпляры ObjectContext (74), медленно растущие, но не экземпляры моего unitOfWork, который держит dbContext. Кажется, что они правильно расположены на основе запроса.
Я знаю, как отделять, прикреплять или изменять состояние записи из моего dbContext, который завернут в unitOfWork, и я часто это делаю. Однако этого недостаточно, или я прошу о невозможности.
Вопросы:
- В принципе, что я делаю неправильно с моим методом кэширования, когда дело доходит до Entity Framework?
- Является ли растущее число объектных контекстов в памяти проблемой, я знаю, что кэш в конечном итоге истечет, но я беспокоюсь о открытых соединениях или чем-то еще, что может иметь этот контекст.
- Должен ли я отделять все от контекста, прежде чем вставлять его в кеш?
- Если да, то какой лучший подход. Особенно со списком я не могу думать ни о чем другом, кроме повторного набора коллекции и вызова отделить один за другим.
- Бонусный вопрос: около 40% потребленной памяти является бесплатной (нераспределенной), я не знаю, почему .NET резервирует так много свободной памяти заранее.