Подтвердить что ты не робот

Имеет ли WeakReference хороший кэш?

У меня есть кеш, который использует WeakReferences для кэшированных объектов, чтобы они автоматически удалялись из кеша в случае давления памяти. Моя проблема в том, что кэшированные объекты собираются очень скоро после того, как они были сохранены в кеше. Кэш работает в 64-битном приложении, и, несмотря на то, что все еще доступно более 4-х гигабайт памяти, все кэшированные объекты собираются (в этот момент они обычно хранятся в G2-куче). Как показывает исследователь процесса, сбор мусора не создается вручную.

Какие методы я могу применить, чтобы объекты продолжали жить дальше?

4b9b3361

Ответ 1

Использование WeakReferences в качестве основного средства ссылки на кешированные объекты на самом деле не является отличной идеей, потому что, как сказал Джош, вы во власти любых будущих поведенческих изменений в WeakReference и GC.

Однако, если ваш кеш требует какой-либо возможности воскрешения, полезно использовать WeakReferences для элементов, ожидающих очистки. Когда элемент соответствует критериям выселения, а не сразу выдает его, вы меняете его ссылку на слабую ссылку. Если что-то запрашивает его до того, как оно GC'ed, вы восстановите его сильную ссылку, и объект может снова жить. Я нашел это полезным для некоторых кэшей, которые трудно предсказать хит-паттернов с достаточно частыми "воскрешениями", чтобы быть полезными.

Если у вас есть предсказуемые шаблоны хит-пойнтов, я бы отказался от опции WeakReference и выполнил явные выселения.

Ответ 2

В .net, WeakReference вообще не считается ссылкой с точки зрения GC, поэтому любой объект, который имеет только слабые ссылки, будет собран в следующем запуске GC (для соответствующего поколения).

Это делает слабую ссылку совершенно неуместной для кеширования - как показывает ваш опыт.

Вам нужен "реальный" кеш-компонент, и самое главное в кэшировании - это получить тот, где политика выселения (то есть правила о том, когда нужно удалить объект из кеша), является подходящим для вас приложением шаблон использования.

Ответ 3

Существует одна ситуация, когда кеш WeakReference может быть хорошим: когда полезность элемента в классе основывается на существовании ссылки на него. В такой ситуации может оказаться полезным слабый интерсинтерный кеш. Например, если бы у кого-то было приложение, которое бы десериализовывало многие большие неизменяемые объекты, многие из которых должны были быть дублирующими и должны были бы выполнять множество сравнений между ними. Если X и Y являются ссылками на некоторый неизменный тип класса, тестирование X.Equals(Y) будет очень быстрым, если обе переменные указывают на один и тот же экземпляр, но могут быть очень медленными, если они указывают на разные экземпляры, которые оказываются равными. Если десериализованный объект соответствует другому объекту, к которому уже существует ссылка, извлечение из словаря ссылки на этот последний объект (требующий медленного сравнения) может ускорить будущие сравнения. С другой стороны, если он сопоставлял элемент в словаре, но словарь был единственной ссылкой на этот элемент, было бы мало пользы от использования словарного объекта вместо простого хранения объекта, который был прочитан; вероятно, недостаточно, чтобы оправдать стоимость сравнения. Для интеркингового кэша, когда WeakReferences становится недействительным как можно скорее, как только никакие другие ссылки не существуют для объекта, было бы хорошо.

Ответ 4

Нет, WeakReference не подходит для этого, потому что поведение сборщика мусора может и будет меняться со временем, и ваш кеш не должен полагаться на сегодняшнее поведение. Также многие факторы вне вашего контроля могут повлиять на давление памяти.

Существует много реализаций кэша для .NET. На CodePlex вы могли бы найти дюжину. Я предполагаю, что вам нужно добавить к нему что-то, что смотрит на текущий рабочий набор приложения, чтобы использовать его как триггер для очистки.

Еще одно замечание о том, почему ваши объекты собираются так часто. GC очень агрессивен при очистке объектов Gen0. Если ваши объекты очень недолговечны (до тех пор, пока единственная ссылка на него не будет слабой ссылкой), GC делает то, что он предназначен, чтобы очистить как можно быстрее.

Ответ 5

Я считаю, что проблема, с которой вы сталкиваетесь, заключается в том, что сборщик мусора удаляет объекты с слабосвязанными объектами в ответ не только в ответ на давление памяти - вместо этого он будет делать сбор довольно агрессивно иногда только потому, что система времени выполнения считает, что некоторые объекты могут, вероятно, стать недоступными.

Вам может быть лучше использовать, например, System.Runtime.Caching.MemoryCache, который может быть настроен с ограничением памяти или пользовательскими политиками выселения для элементов.

Ответ 6

Ответ на самом деле зависит от характеристик использования кэша, который вы пытаетесь построить. Я успешно использовал стратегию кэширования на основе WeakReference для повышения производительности во многих моих проектах, где ожидается, что кэшированные объекты будут использоваться в коротких очередях с несколькими чтениями. Как указывали другие, слабые ссылки в значительной степени мусорны с точки зрения GC и будут собираться всякий раз, когда запускается следующий цикл GC. Это не связано с использованием памяти.

Если, однако, вам нужен кеш, который выживает из такой жестокости с GC, вам нужно использовать или имитировать функции, предоставляемые пространством имен System.Runtime.Caching. Имейте в виду, что вам понадобится дополнительный поток, который очищает кеш, когда использование памяти пересекает ваши пороговые значения.

Ответ 7

Немного поздно, но здесь применимый прецедент:

Мне нужно кэшировать два типа объектов: большие (десериализованные) файлы данных, которые занимают 10 минут для загрузки и стоят 15 ГБ каждого из них, и меньшие (динамически скомпилированные) объекты, которые содержат внутренние ссылки на эти файлы данных (меньшие объекты также кэшируются, потому что они берут ~ 10 с для генерации). Эти кеши скрыты на фабриках, которые снабжают объекты (прежний компонент не знает последних) и имеют разные политики выселения.

Когда кеш "файла данных" вытесняет объект, он заменяет его слабой ссылкой, поэтому, если этот объект по-прежнему доступен при следующем запросе, мы можем его воскресить (и возобновить его тайм-аут кеша). Таким образом, мы избегаем потери (или случайного дублирования) какого-либо объекта, прежде чем он станет действительно несуществующим (т.е. Нигде не используется). Обратите внимание, что ни один кэш не должен знать о другом, и что никакие другие объекты-клиенты не должны знать, что есть какие-либо кеши вообще (например: мы избегаем необходимости "keepalives", обратных вызовов, областей регистрации, получения и возврата, и т.д. - все становится намного проще).

Итак, хотя использование WeakReference само по себе (вместо кеша) является ужасной идеей (поскольку современные GC обычно настраиваются на размер кэша процессора L2, и обычный код будет гореть через это много раз в минуту), это очень полезно как способ скрыть ваши кеши от остальной части вашего кода.

Ответ 8

Я подозреваю, что он собирается спросить о кеше SoftReference (не WeakReference). Я не могу найти его прямо сейчас, но я помню, как читал от кого-то, кто внедрил частную JVM, умоляя людей не использовать их. Он утверждал, что эти кэши предполагают, что автор сборщика мусора каким-то образом знает о ваших потребностях в кэшировании больше, чем вы (что никогда не должно быть правдой).

Когда я вспомнил, что в тот день, когда цикл GC не освободил достаточно памяти, все SoftReferences впоследствии были очищены сразу, т.е. кэш переходит из смехотворного (нехватка памяти) в полный (одинаково нелепый) полностью пустой. Вместо этого выберите реализацию кеша, которая работает в зависимости от размера, возраста или того и другого, то есть той, которой вы можете дать свои разумные правила, вместо того, чтобы пытаться переложить решение о том, как работает ваш кеш, на человека, который написал сборщик мусора для некоторая причина.