Я лично не сталкивался с ситуацией, когда мне нужно было использовать тип WeakReference в .Net, но распространенное мнение, похоже, заключается в том, что его следует использовать в кэш-памяти. Доктор Джон Харроп привел очень хороший аргумент против использования WeakReferences в кешах в своем ответе на этот вопрос.
Я также часто слышал, как разработчики AS3 говорили об использовании слабых ссылок для экономии занимаемой памяти, но, исходя из моих разговоров, кажется, что это добавляет сложности, не обязательно достигая намеченной цели, а поведение во время выполнения довольно непредсказуемо. Настолько, что многие просто отказываются от этого и вместо этого более осторожно управляют использованием памяти / оптимизируют свой код, чтобы он занимал меньше памяти (или делает компромисс между большим количеством циклов ЦП и меньшим объемом памяти).
Доктор Джон Харроп также указал в своем ответе, что слабые ссылки .Net не являются мягкими, и существует агрессивная коллекция слабых ссылок на gen0. Согласно MSDN , длинные слабые ссылки дают вам возможность воссоздать объект but the state of the object remains unpredictable.
!
Учитывая эти характеристики, я не могу вспомнить ситуацию, когда слабые ссылки были бы полезны, возможно, кто-то мог бы просветить меня?
источник
Ответы:
Я нашел законное практическое применение слабых ссылок в следующих трех реальных сценариях, которые фактически произошли со мной лично:
Приложение 1: обработчики событий
Вы предприниматель. Ваша компания продает контрольные линии зажигания для WPF. Продажи отличные, но расходы на поддержку убивают вас. Слишком много клиентов жалуются на перегрузку процессора и утечки памяти при прокрутке экранов, заполненных линиями искр. Проблема в том, что их приложение создает новые линии искры, когда они появляются, но привязка данных не позволяет собирать старые. Чем ты занимаешься?
Введите слабую ссылку между привязкой данных и вашим элементом управления, чтобы одна только привязка данных больше не препятствовала сборке мусора для вашего элемента управления. Затем добавьте финализатор к вашему элементу управления, который разрушает привязку данных, когда они собираются.
Приложение 2: изменяемые графы
Ты следующий Джон Кармак. Вы изобрели новое графическое представление поверхностей иерархического подразделения, которое делает игры Тима Суини похожими на Nintendo Wii. Очевидно, я не собираюсь рассказывать вам точно, как это работает, но все это сосредоточено на этом изменяемом графе, где соседи вершины могут быть найдены в
Dictionary<Vertex, SortedSet<Vertex>>
. Топология графа постоянно меняется, пока игрок бегает. Есть только одна проблема: ваша структура данных сбрасывает недостижимые подграфы во время работы, и вам нужно удалить их, иначе произойдет утечка памяти. К счастью, вы гений, так что вы знаете, что существует класс алгоритмов, специально предназначенных для поиска и сбора недоступных подграфов: сборщики мусора! Вы читаете отличную монографию Ричарда Джонса на эту темуно это оставляет вас озадаченным и обеспокоенным вашим предстоящим сроком. Чем ты занимаешься?Просто заменив вашу
Dictionary
слабую хеш-таблицу, вы можете использовать существующий GC и автоматически собирать недостижимые для вас подграфы! Вернуться к листанию рекламы Ferrari.Приложение 3: Украшение деревьев
Вы свисаете с потолка циклической комнаты за клавиатурой. У вас есть 60 секунд, чтобы просмотреть БОЛЬШИЕ ДАННЫЕ, прежде чем вас найдут. Вы подготовили прекрасный потоковый парсер, который полагается на сборщик мусора для сбора фрагментов AST после их анализа. Но вы понимаете, что вам нужны дополнительные метаданные для каждого AST,
Node
и вам это нужно быстро. Чем ты занимаешься?Вы можете использовать a
Dictionary<Node, Metadata>
для связи метаданных с каждым узлом, но, если вы не очистите их, сильные ссылки из словаря на старые узлы AST сохранят их живыми и утечку памяти. Решением является слабая хеш-таблица, в которой хранятся только слабые ссылки на ключи, а сборщик мусора собирает привязки ключ-значение, когда ключ становится недоступным. Затем, когда узлы AST становятся недоступными, они подвергаются сборке мусора, и их привязка значения ключа удаляется из словаря, оставляя соответствующие метаданные недоступными, поэтому они тоже собираются. Затем все, что вам нужно сделать после того, как ваша основная петля прервана, это проскользнуть обратно через вентиляционное отверстие, не забывая заменить его, как только войдет охранник.Обратите внимание, что во всех трех реальных приложениях, которые действительно произошли со мной, я хотел, чтобы сборщик мусора собирал как можно более агрессивно. Вот почему это законные приложения. Все остальные не правы.
источник
ConditionalWeakTable
в .NET).ConditionalWeakTable
это то, что приложения 2 и 3 будут использовать, в то время как некоторые люди в других постах на самом деле используютDictionary<WeakReference, T>
. Понятия не имею, почему - у вас всегда будет тонна нулевыхWeakReference
значений со значениями, к которым ни один ключ не может получить доступ, независимо от того, как вы это делаете. Ridik.Документ Microsoft Слабые шаблоны событий .
источник
Позвольте мне сначала высказать это и вернуться к этому:
Итак, начнем с самого начала:
- заранее извиняюсь за любое непреднамеренное оскорбление, но я ненадолго вернусь к уровню "Дик и Джейн", поскольку никто никогда не сможет рассказать об этом аудитории.
Поэтому, когда у вас есть объект
X
- давайте определим его как экземплярclass Foo
- он НЕ МОЖЕТ жить самостоятельно (в основном это правда); Точно так же, как «Ни один человек не является островом», есть только несколько способов, которыми объект может быть переведен в Islandhood - хотя это называется быть корнем GC в CLR. Быть корнем GC или иметь установленную цепочку соединений / ссылок на корень GC - это, в основном, то, что определяет,Foo x = new Foo()
собирается ли мусор или нет .Если вы не можете вернуться к какому-либо корню GC с помощью кучи или стека, вы фактически осиротели и, вероятно, будете отмечены / собраны в следующем цикле.
На данный момент, давайте посмотрим на некоторые ужасно надуманные примеры:
Во-первых, наши
Foo
:Довольно просто - это не потокобезопасно, так что не пытайтесь это сделать, но сохраняете приблизительный «счетчик ссылок» активных экземпляров и декрементов, когда они завершаются.
Теперь давайте посмотрим на
FooConsumer
:Итак, у нас есть объект, который уже является собственным корнем GC (ну ... если быть точным, он будет внедряться через цепочку прямо в домен приложения, в котором выполняется это приложение, но это уже другая тема), который имеет два метода привязки к
Foo
экземпляру - давайте проверим это:Теперь, исходя из вышесказанного, ожидаете ли вы, что объект, на который когда-то ссылались,
f
будет «коллекционируемым»?Нет, потому что есть другой объект, который теперь содержит ссылку на него -
Dictionary
в этомSingleton
статическом экземпляре.Хорошо, давайте попробуем слабый подход:
Теперь, когда мы ударили нашу ссылку на «это
Foo
было когда-f
то», больше нет «жестких» ссылок на объект, так что его можно собрать -WeakReference
созданный слабым слушателем не помешает этому.Хорошие варианты использования:
Обработчики событий (хотя сначала прочтите это: Слабые события в C # )
У вас есть ситуация, когда вы вызываете «рекурсивную ссылку» (т.Е.Объект A ссылается на объект B, который ссылается на объект A, также называемый «утечкойпамяти»)(edit: derp, конечно, это не не правда)Вы хотите «вещать» что-то на коллекцию объектов, но не хотите, чтобы они поддерживали их жизнь;
List<WeakReference>
может поддерживаться легко, и даже обрезают, удаляя , гдеref.Target == null
источник
Например, логические утечки, которые действительно трудно отследить, в то время как пользователи просто замечают, что длительное использование вашего программного обеспечения, как правило, занимает все больше и больше памяти и становится все медленнее и медленнее, пока они не перезапустятся? Я не.
Рассмотрим, что произойдет, если, когда пользователь запросит удалить ресурс приложения, указанный выше,
Thing2
не сможет правильно обработать такое событие в:... и при которой одна из этих ошибок, скорее всего, будет обнаружена во время тестирования, а другая - нет, и она полетела бы под радаром, как ошибка истребителя-невидимки. Совместное владение чаще всего бессмысленная идея.
источник
Очень наглядным примером слабых ссылок, используемых для хорошего эффекта, является ConditionalWeakTable , который используется DLR (среди других мест) для присоединения дополнительных «элементов» к объектам.
Вы не хотите, чтобы стол поддерживал объект в живых. Эта концепция просто не могла работать без слабых ссылок.
Но мне кажется, что все случаи использования слабых ссылок появились после того, как они были добавлены в язык, так как слабые ссылки были частью .NET начиная с версии 1.1. Это просто то, что вы хотели бы добавить, так что отсутствие детерминированного разрушения не загонит вас в угол, если говорить о языковых особенностях.
источник
WeakReference
тип, так как ситуация намного сложнее. Он использует различные функциональные возможности, предоставляемые CLR.Если у вас есть слой кеша, реализованный на C #, гораздо лучше поместить ваши данные в кеш как слабые ссылки, это может помочь улучшить производительность вашего слоя кеша.
Думаю, что этот подход также может быть применен к реализации сессии. Поскольку сеанс является долгоживущим объектом большую часть времени, это может быть случай, когда у вас нет памяти для нового пользователя. В этом случае будет гораздо лучше удалить еще один объект сеанса пользователя, чем выбрасывать исключение OutOfMemoryException.
Кроме того, если у вас есть большой объект в вашем приложении (какая-то большая справочная таблица и т. Д.), Его следует использовать довольно редко, и воссоздание такого объекта не является очень дорогой процедурой. Тогда лучше иметь недельный справочник, чтобы освободить память, когда она вам действительно нужна.
источник