Pull to refresh

Comments 9

Может быть я невнимательно читал и статья совсем про другое, но я так и не увидел — что происходит с инвалидированными объектами, выкинутыми из кеша, но все еще остающимися в памяти? Висят во втором поколении мертвым грузом, ожидая своего stop the world секунд на несколько?

Имхо это одна из основных проблем для реализации кешей в средах с автоматическим управлением памятью. В недавней статье на хабре на эту тему предлагалось решение через сериализацию в длинные массивы. Ожидал увидеть тут какую-то альтернативу.
Может быть я невнимательно читал и статья совсем про другое

Статья действительно немного о другом. Я не ставил за цель открыть все подробности реализации MemoryCache. У меня было две цели:
1) дать вводную информацию про что такое кеш и некоторые советы по тому как правильней организовать кеширование
2) отрыть некоторые грабли, на которые обязательно наткнёшься при начале использования MemoryCache.

что происходит с инвалидированными объектами

Попробую ответить, основываясь на представлений о работе CLR и на беглом исследовании исходного кода (т. к. документация об этих вещах умалчивает).
Сам класс не старается управлять памятью напрямую, используя неуправляемый (unmanaged) код. Его основной задачей связанной с памятью является гарантирование, что ссылки на элементы будут удалены из кеша согласно «политикам протухания (expiration)», а так же в случае достижения лимитов по разрешённой памяти. На этом его работа считается оконченной и в игру вступает Garbage Collector. А тот уже удаляет объекты согласно алгоритму, основанном на поколениях. И получается, что если элемент в кеше действительно пережил две сборки, то он будет во втором поколении и может залипнуть там на некоторое время. Ведь как вы правильно заметили, то мы работаем в среде с автоматическим управлением памяти, и получается что это уже особенность среды (как она управляет памятью), а не конкретной реализации кеша.
Вообще, если задуматься, как это можно было бы реализовать иначе, то мне в голову приходят только крайне сложные решения:
— либо всё на unmanaged коде
— либо как-то так, что бы класс управлял логикой работы GC. Например, оставлял объекты в специальном поколении, которое можно было бы проверить, после того, как кеш удалил объекты. Но насколько я знаю, пока GC не даёт такой возможности и специально для класса MemoryCache его не будут наделять такими возможностями.
По итогу, если хотим избежать задержки, то мы должны перенимать на себя заботу о своевременной очистке памяти. Но это уже попахивает написанием своего GC. Поэтому я бы просто смерился, что будет некая задержка, прежде, чем ненужные значения из кеша фактически удалятся из памяти.
И последнее. Я думаю, что перерасход памяти и задержка не должны быть очень большими. Если у нас в приложении активно что-то кешиться, то наши 0-е и 1-е поколения будут всегда быстро заполняться и приводить к сборке во 2-м поколении. Так что по факту overhead должен быть минимальным.
Спасибо. Я имел в виду достаточно распространенные сценарии когда в кеш подгружается много объектов периодически (раз в Х минут). И как правило переживают несколько сборок мусора прежде чем «протухнут». В итоге у нас второе поколение забито миллионами объектов что радикально увеличивает время сборки в этом поколении.

Хорошего универсального способа обойти эти грабли я к сожалению не нашел.
Походу я не сразу понял проблему, которую вы описывали. Но тепрь надеюсь смогу ответить на заданный вопрос :).
У MemoryCache будет проблема, о которой вы спрашиваете. Внутри данные хранятся во множестве объектов типа Hashtable. Т.е. никаких трюков с сериализацией и утаивания существующих объектов от GC не происходит.
описанные решения не позволяют хранить сотни миллионов объектов долго — они убивают ГЦ
мы решили ети проблемы с помощию Пиле — который описан тут:

habrahabr.ru/post/257091

youtu.be/Dz_7hukyejQ

в наших преложениях спокоино можно держать 200-300 миллионов объектов хоть 3 месяца и сборшик мусора < 50 мс
Code:

github.com/aumcode/nfx/blob/master/Source/NFX/ApplicationModel/Pile/ICache.cs
Ваше решение я и имел ввиду в первом комментарии. Выглядит громоздко, но ничего лучше я пока не нашел.
в C# был допушен просчет — отсутствие гибрдинои памяти. они ето сделали для упрошения
платформы, но им ничего не стоило хранитb те-же манагед objects в unmanaged heap через например
оператор unmanaged new

var x = unmanaged new Person();

именно это мы делаем в нашем языке Аум:

var pool1 = new Pool() at Area1;

var obj1 = new Person() at pool1;

pool1.Delete(); //deallocates eberything instantly

========================================
coming back to C#:

это бы опять подорвало 3ашиты от дурака — ибо вернулись бы все прелести ц++, но дело не в этом.

етого нет и не будет скорее всего никогда в ЦЛР.

поетому Пиле — это самое елегантное что вы сможете найти для работы с ЛЮБыми типами (а не только рукописними в быте [])
Рискну предположить что это был не прощёт, а тщательно продуманное решение. Ведь за гибкостью и большим количеством возможностей обычно таится сложность.
При разработке языка / платформы не пытались сделать что-то, подходящее на все случаи жизни. Они старались сделать то, что подойдёт для большинства и будет максимально удобным.
Возможно когда-то это будет добавлено, но до сегоднешнего дня находились более интересные направления развития. Например, TPL, async/await, Roslyn и т.п.
Также хочу вспомнить старую проблему нулевых ссылок (подробней здесь). Не буду утверждать, что сравнение 100% адекватное, но это пример того, как дополнительная возможность может порождать больше зла, чем добра. Вроде уже даже активно обсуждается добавление в C# инструментов, которые позволять создавать ссылочные типы, но не иметь возможности хранить NULL в ссылочной переменной. Т.е. я намекаю на то, что выбирая между добавлением описанной вами фичи и её отсутсвием, Microsoft, взвесив все за и против, решила её не реализовывать, боясь породить больше зла. Верни время назад, они бы скорее всего не реализовывали возможность хранения NULL значний в ссылочных переменных.
Мысли такие потому, что часто читал статьи Эрика (Eric Lippert), в которых он рассказывал, как MS скурпулёзно подходит к выбору фич и возможностей языка и платформы. Он многократно на пальцах объяснял, как добавление какой-то «всеми долго желанной» фичи на самом деле не имеет смысла, по причине «большего зла».
Sign up to leave a comment.