Циклические ссылки
Циклические ссылки
Циклические ссылки являются источником практически любой утечки. Обычно скриптовые движки нормально отрабатывают с циклическими ссылками при помощи собственных сборщиков мусора, однако из-за некоторых неопределенностей их механизм эвристических правил может дать сбой. Одной из таких неопределенностей будет состояние DOM-объекта, к которому имеет доступ текущая порция скрипта. Основной принцип в данном случае можно описать так:
Рис. 7.3. Основной шаблон циклической ссылки
Утечка в таком шаблоне происходит из-за особенностей учета DOM-ссылок. Объекты скриптового движка удерживают ссылку на DOM-элемент и ожидают, пока будут освобождены все внешние ссылки, чтобы освободить, в свою очередь, этот указатель на DOM-элемент. В нашем случае у нас две ссылки на объект скрипта: внутри области видимости скриптового движка и от расширенного свойства DOM-элемента. По окончанию своей работы скрипт освободит первую ссылку, но ссылка из DOM-элемента никогда не будет освобождена, потому что ждет, что это сделает объект скрипта!
Наверное, первой же мыслью будет, что такой сценарий развития событий легко обнаружить и устранить, однако на практике представленный базовый случай является только вершиной айсберга. Может оказаться так, что циклическая ссылка находится в конце цепочки из 30 объектов, а обнаружить ее при этом крайне тяжело.
Стоит посмотреть, как данный шаблон будет выглядеть в HTML. Это может вызвать утечку, используя глобальную переменную и DOM-объект, как показано ниже.
<script type="text/javascript">
var myGlobalObject;
function SetupLeak()
{
// Для начала создадим ссылку из скрипта на DOM-элемент
myGlobalObject = document.getElementById("LeakedDiv");
// Потом установим ссылку из DOM на глобальную переменную
document.getElementById("LeakedDiv").expandoProperty =
myGlobalObject;
}
function BreakLeak()
{
document.getElementById("LeakedDiv").expandoProperty = null;
}
window.onload = SetupLeak;
window.onunload = BreakLeak;
</script>
Чтобы разрушить этот шаблон, можно использовать явное присвоение null тому свойству, которое «течет». Таким образом, при закрытии документа мы сообщаем скриптовому движку, что между DOM-элементом и глобальной переменной нет больше никакой связи. В результате все ссылки будут очищены, и сам DOM-элемент будет освобожден. В таком случае веб-разработчик знает больше о внутренних отношениях между объектами, чем сам скрипт, и может поделиться этой информацией со скриптом.