Почему слабые указатели полезны?
Я читал сборку мусора, ища функции, которые можно включить в мой язык программирования, и я наткнулся на "слабые указатели". Из здесь:
Слабые указатели похожи на указатели, за исключением того, что ссылки от слабых указатели не мешают мусору сбор и слабые указатели должны проверить их действительность до они используются.
Слабые указатели взаимодействуют с сборщик мусора, потому что память к которым они относятся, на самом деле может действительны, но содержат объект, чем это было, когда слабые указатель был создан. Таким образом, всякий раз, когда сборщик мусора перерабатывает память, он должен проверить, есть ли слабые указатели, ссылающиеся на него, и пометить их как недействительные (это необязательно реализованный таким наивным образом).
Я никогда не слышал о слабых указателях раньше. Я хотел бы поддержать многие функции на моем языке, но в этом случае я не могу для жизни меня думать о случае, когда это было бы полезно. Для чего использовать слабый указатель?
Ответы
Ответ 1
Типичным примером использования является хранение дополнительных атрибутов объекта. Предположим, у вас есть класс с фиксированным набором элементов, а снаружи вы хотите добавить больше членов. Таким образом, вы создаете атрибут словаря → атрибуты, где ключи являются слабыми ссылками. Тогда словарь не предотвращает сбор мусора от ключей; удаление объекта также должно приводить к удалению значений в WeakKeyDictionary (например, посредством обратного вызова).
Ответ 2
Действительно большой - кеширование. Подумайте, как будет работать кеш:
Идея, лежащая в основе кеша, заключается в том, чтобы хранить объекты в памяти до тех пор, пока давление в памяти не станет настолько большим, что некоторые объекты должны быть вытолкнуты (или явно недействительны, конечно). Таким образом, ваш объект репозитория кеша должен как-то держаться за эти объекты. Держась за них через слабую ссылку, когда сборщик мусора ищет вещи, которые нужно потреблять, потому что память низкая, предметы, на которые ссылается только слабая ссылка, будут отображаться как кандидаты на сбор мусора. Элементы в кеше, которые в настоящее время используются другим кодом, будут иметь жесткие ссылки, все еще активные, поэтому эти элементы будут защищены от сбора мусора.
В большинстве ситуаций вы не будете запускать свой собственный механизм кэширования, но обычно используется кеш. Предположим, вы хотите иметь свойство, которое ссылается на объект в кеше, и это свойство остается в области в течение длительного времени. Вы предпочитаете извлекать объект из кеша, но если он недоступен, вы можете получить его из сохраненного хранилища. Вы также не хотите, чтобы этот конкретный объект оставался в памяти, если давление становится слишком высоким. Таким образом, вы можете использовать слабую ссылку на этот объект, который позволит вам извлечь его, если он доступен, а также позволить ему выпасть из кеша.
Ответ 3
Если ваш сборщик мусора на языке не способен собирать круговые структуры данных, вы можете использовать слабые ссылки, чтобы позволить ему это делать. Обычно, если у вас есть два объекта, которые имеют ссылки друг на друга, но ни один другой внешний объект не имеет ссылки на эти два, они будут кандидатами на сбор мусора. Но наивный сборщик мусора не собирал их, поскольку они содержат ссылки друг на друга.
Чтобы исправить это, вы делаете так, чтобы один объект имел сильную ссылку на вторую, а второй - слабую ссылку на первую. Затем, когда последняя внешняя ссылка на первый объект уходит, первый объект становится кандидатом на сбор мусора, а затем вскоре после второго, так как теперь его единственная ссылка слаба.
Ответ 4
Другой пример... не совсем кэширование, но похоже: предположим, что библиотека ввода-вывода предоставляет объект, который обертывает файловый дескриптор и разрешает доступ к файлу. Когда объект собран, дескриптор файла закрыт. Желательно иметь возможность перечислять все открытые в данный момент файлы. Если вы используете сильные указатели для этого списка, файлы никогда не закрываются.
Ответ 5
Используйте их, когда вы хотите сохранить кешированный список объектов, но не мешайте этим объектам получать сбор мусора, если с ним выполняется "настоящий" владелец объекта.
Веб-браузер может иметь объект истории, который поддерживает ссылки на объекты изображений, которые браузер загружает в другое место и сохраняется в кеше истории/диска. Веб-браузер может истечь одним из этих изображений (пользователь очистил кеш, истек тайм-аут кэша и т.д.), Но на странице все равно будет указатель/указатель. Если на странице используется слабый указатель/указатель, объект будет уходить, как ожидалось, и память будет собираться мусором.
Ответ 6
Одной из важных причин наличия слабых ссылок является рассмотрение возможности того, что объект может служить конвейером для подключения источника информации или событий к одному или нескольким слушателям. Если нет слушателей, нет причин продолжать отправку информации в конвейер.
Рассмотрим, например, перечислимую коллекцию, которая допускает обновления во время перечисления. Коллекции могут потребоваться уведомить любые активные счетчики о том, что они были изменены, поэтому эти счетчики могут соответствующим образом скорректировать себя. Если некоторые перечислятели заброшены их создателями, но коллекция содержит ссылки на них, эти счетчики будут продолжать существовать (и уведомлять об обновлении процесса) до тех пор, пока существует коллекция. Если сама коллекция будет существовать в течение всего срока службы приложения, эти счетчики фактически станут постоянной утечкой памяти.
Если коллекция содержит слабые ссылки на счетчики, эта проблема может быть решена в значительной степени. Если перечислитель отказался, он будет иметь право на сбор мусора, хотя коллекция все еще имеет слабую ссылку на нее. В следующий раз, когда коллекция будет изменена, она может просмотреть список слабых ссылок, отправить обновления тем, которые все еще действительны, и удалить из своего списка те, которые не являются.
Можно было бы достичь многих эффектов слабых ссылок с использованием финализаторов вместе с некоторыми дополнительными объектами, и это могло бы сделать такие реализации более эффективными, чем те, которые используют слабые ссылки, но есть много ошибок, и трудно избежать ошибок, Гораздо проще сделать правильный подход, используя WeakReference. Этот подход может быть неэффективным, но он не будет терпеть неудачу.
Ответ 7
Слабые указатели сохраняют все, что заставляет их становиться формой "поддержки жизни" для объекта, на который указывает указатель.
Предположим, что у вас есть класс Viewport и 2 класса пользовательского интерфейса, а также класс классов Widget. Вы хотите, чтобы ваш пользовательский интерфейс контролировал срок действия создаваемых ими виджетов, поэтому ваш пользовательский интерфейс сохраняет SharedPtrs во всех виджетах, которыми он управляет. До тех пор, пока ваш объект пользовательского интерфейса жив, ни один из виджетах, которые он показывает, будет собираться мусором (благодаря SharedPtr).
Тем не менее, Viewport - это ваш класс, который на самом деле делает чертеж, поэтому ваш пользовательский интерфейс должен передать указателю указатель на виджеты, чтобы он мог их рисовать. По какой-либо причине вы хотите изменить свой активный пользовательский интерфейс на другой. Давайте рассмотрим два сценария: один, где пользовательский интерфейс передал Viewport WeakPtrs и тот, где он передал SharedPtrs (указав на виджеты).
Если вы прошли через Viewport все виджеты как WeakPointers, как только был удален класс пользовательского интерфейса, в Виджеты не будет больше SharedPointers, поэтому они будут собирать мусор, ссылки Viewport на объекты не будут сохраняться их на "жизненной поддержке", что именно то, что вы хотите, потому что вы даже не используете этот интерфейс, а тем более созданные Widgets.
Теперь подумайте, что вы прошли через Viewport a SharedPointer, вы удаляете пользовательский интерфейс, а виджеты НЕ собираются сбрасывать мусор! Зачем? потому что Viewport, который все еще жив, имеет массив (вектор или список, что угодно), полный SharedPtrs, к виджетам. Фактически Viewport стал для них "поддержкой жизни", хотя вы удалили пользовательский интерфейс, который управлял виджетами для другого объекта пользовательского интерфейса.
Как правило, язык/система/фреймворк будет мусор собирать что-либо, если нет "сильной" ссылки на него где-то в памяти. Представьте себе, если бы все имело сильную ссылку на все, ничто не собиралось собирать мусор! Иногда вы этого поведения иногда не замечаете. Если вы используете WeakPtr и нет объектов Shared/StrongPtrs слева, указывающих на объект (только WeakPtrs), тогда объекты будут собирать мусор, несмотря на ссылки WeakPtr, а WeakPtrs (должен быть) установлен в NULL (или удален или что-то).
Опять же, когда вы используете WeakPtr, вы в основном позволяете объекту, который вы ему даете, чтобы иметь возможность доступа к данным, но WeakPtr не предотвратит сбор мусора объекта, на который он указывает, как SharedPtr., Когда вы думаете, что SharedPtr, подумайте о "поддержке жизни", WeakPtr, NO "поддержка жизни". Сбор мусора не будет (обычно) до тех пор, пока объект не окажет нулевого срока службы.
Ответ 8
Слабые ссылки могут использоваться, например, в сценариях кэширования - вы можете получить доступ к данным через слабые ссылки, но если вы не будете долгое время обращаться к данным или есть высокое давление в памяти, GC может освободить его.
Ответ 9
Причина сбора мусора вообще заключается в том, что на языке C, где управление памятью полностью находится под явным контролем программиста, когда передается владение объектом, особенно между потоками или, что еще труднее, между процессами, использующими память, избегая утечки памяти и оборванные указатели могут стать очень трудными. Если это не так сложно, вам также придется иметь дело с необходимостью иметь доступ к большему количеству объектов, чем будет вписываться в память за один раз - вам нужно иметь способ освободить некоторые объекты на некоторое время, чтобы другие объекты может быть в памяти.
Итак, некоторые языки (например, Perl, Lisp, Java) предоставляют механизм, в котором вы можете просто прекратить "использование" объекта, и сборщик мусора в конечном итоге обнаружит это и освободит память, используемую для объекта. Он делает это правильно, не программист, беспокоясь о том, как они могут ошибаться (хотя есть много способов, которыми программисты могут это испортить).
Если вы концептуально умножаете количество раз, когда вы обращаетесь к объекту, к тому времени, которое требуется для вычисления значения объекта, и, возможно, снова умножить на стоимость отсутствия объекта, легко доступного или размера объекта так как хранение большого объекта вокруг в памяти может предотвратить сохранение нескольких объектов меньшего размера, вы можете классифицировать объекты по трем категориям.
Некоторые объекты настолько важны, что вы хотите явно управлять своим существованием - они не будут управляться сборщиком мусора, или они никогда не должны собираться до явного освобождения. Некоторые объекты дешевы для вычисления, являются небольшими, не имеют доступа часто или имеют схожие характеристики, которые позволяют им собирать мусор в любое время.
Третий класс, объекты, которые дорого перечитывать, но могут быть пересчитаны, доступны несколько чаще (возможно, за короткий промежуток времени), имеют большой размер и т.д. являются третьим классом. Вы хотели бы сохранить их в памяти как можно дольше, потому что они могут быть повторно использованы повторно, но вы не хотите, чтобы нехватка памяти была необходима для критических объектов. Это кандидаты на слабые ссылки.
Вы хотите, чтобы эти объекты поддерживались как можно дольше, если они не конфликтуют с критическими ресурсами, но они должны быть удалены, если память необходима для критического ресурса, потому что при необходимости ее можно повторно пересчитать. Это слабые указатели на шляпу.
Примером этого могут быть изображения. Скажем, у вас есть веб-страница с фотографиями, на которой отображаются тысячи изображений. Вам нужно знать, сколько картинок выложить, и, возможно, вам нужно сделать запрос базы данных, чтобы получить список. Память для хранения списка из нескольких тысяч предметов, вероятно, очень мала. Вы хотите сделать запрос один раз и сохранить его.
Вы можете физически показывать, возможно, несколько десятков снимков за раз, однако, на панели веб-страницы. Вам не нужно извлекать биты для изображений, на которые пользователь не может смотреть. Когда пользователь прокручивает страницу, вы будете собирать фактические биты для видимых изображений. Этим изображениям может потребоваться много мегабайт, чтобы показать их. Если пользователь прокручивается вперед и назад между несколькими положениями прокрутки, вам не нужно снова и снова перезапускать эти мегабайты. Но вы не можете держать все изображения в памяти все время. Поэтому вы используете слабые указатели.
Если пользователь просто просматривает несколько снимков снова и снова, они могут оставаться в кеше, и вам не нужно их переучивать. Но если они достаточно прокручиваются, вам нужно освободить память, чтобы можно было увидеть видимые изображения. При слабой ссылке вы проверяете ссылку непосредственно перед ее использованием. Если он все еще действителен, вы его используете. Если его нет, вы делаете дорогой расчет (выборку), чтобы получить его.