Предотвращение утечки циклической эталонной памяти в Perl

Недавно я спросил question о перезаписи объектов и управления памятью в Perl. Один из ответов, которые я получил, уведомил меня, что у меня может возникнуть проблема с script, который я недавно написал.

У меня есть script с некоторыми очень сложными структурами данных, которые имеют много отношений parent->child / child->parent. Это также означает, что существует много объектов, которые имеют циклические ссылки. В соответствии с этим ответом циклические ссылки могут "обмануть" механизм подсчета ссылок Perl и вызвать утечку памяти, если они не обрабатываются должным образом.


Пример циклической ссылки:

       +-----------------------------------------------------+
       |                                                     |
       +-->+============+    +==========+                    |
           [ Reference ----->[ Blessed  ]                    |
$parent -->+============+    [ Hash     ]                    |
                             [          ]   +==========+     |
                             [ children --->[ Array    ]     |
                             [          ]   [          ]     |
                             +==========+   [ 0: ---------+  |
                                            [          ]  |  |
                                            +==========+  |  |
                                                          |  |
       +--------------------------------------------------+  |
       |                                                     |
       +-->+============+    +==========+                    |
           [ Reference ----->[ Blessed  ]                    |
$child --->+============+    [ Hash     ]                    |
                             [          ]                    |
                             [ parent: ----------------------+
                             [          ]
                             +==========+

(Отказ от ответственности - это не мое эпическое произведение - спасибо @Ikegami за эту сладкую диаграмму ASCII!)

Проблема: Каждый объект имеет ссылку на другую., это означает, что после того, как $parent и $child исчезнут из области видимости, счетчик ссылок Perl все еще считает, что ссылка на каждый объект существует, поэтому память никогда не освобождается. Вы завершаете работу с двумя объектами в памяти, чтобы не получить доступ к данным ни одного из них!


Мой вопрос: Каков правильный способ обработки циклических ссылок, чтобы Perl правильно обрабатывал его очистку? Как вы убеждаетесь, что Perl не оставляет никаких деталей, когда устраняются все внешние ссылки на самореферентный объект?

Ответ 1

Scalar::Util и, в частности, функцию weaken.

lvalue $ref будет переведен в слабую ссылку. Это означает, что он не будет содержать счетчик ссылок на объект, который он ссылается. Также, когда счетчик ссылок на этот объект достигнет нуля, ссылка будет установлена ​​на undef. Эта функция мутирует значение lvalue, переданное как его аргумент, и не возвращает значения.

Настройте один или оба из ваших ссылок как "слабый", а цепочка маргаритов автоматически раскроется, когда анкеры будут разрушены.