Выбрать главу

Псевдо-утечки

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

Так же как и ситуация, связанная с добавлением элементов в DOM-дерево, эта проблема опирается на создание временных объектов и приводит к «съеданию» памяти. Переписывая текстовый узел внутри скриптового элемента раз за разом, можно наблюдать, как количество доступной памяти мало-помалу уменьшается из-за различных объектов внутреннего движка, которые были привязаны к предыдущему содержанию. В частности, позади остаются объекты, отвечающие за отладку скриптов, поскольку они полностью принадлежат предыдущему куску кода.

<script type="text/javascript">

function LeakMemory()

{

// Посмотрим, что происходит с памятью в Диспетчере Задач

for(i = 0; i < 5000; i++)

{

hostElement.text = "function foo() { }";

}

}

</script>

<button onclick="LeakMemory()">Вставить с утечками памяти</button>

<script id="hostElement">function foo() { }</script>

Если мы запустим приведенный код и посмотрим в Диспетчере Задач, что происходит при переходе с «текущей» страницы на чистую, то не увидим никаких утечек. Скрипт расходует память только внутри текущей страницы, и при перемещении на новую вся задействованная память разом освобождается. Ошибка заключается в неверном ожидании определенного поведения. Казалось бы, что переписывание некоторого скрипта приведет к тому, что предыдущий кусок будет бесследно исчезать, оставляя только дополнительные циклические ссылки или замыкания, однако фактически он не исчезает. Очевидно, это псевдо-утечка. В данном случае размер выделенной памяти выглядит устрашающе, но для этого имеется совершенно законная причина.

Проектируем утечки

Каждый веб-разработчик составляет персональный список примеров кода, для которого известно, что он «течет», и пытается найти для каждого случая достойное решение, когда обнаруживает источник проблемы. Это весьма полезно, и именно по этой причине сейчас веб-страницы относительно свободны от утечек памяти. Размышляя о проблемах выделения памяти в терминах шаблонов, а не индивидуальных кусков кода, можно начать внедрять гораздо более продуктивные и более осмысленные решения.

Идея заключается в том, чтобы уже на этапе проектирования приложения мы имели представление о том, какие утечки возможны и как с ними будет лучше работать. Используйте «оборонительную» тактику при разработке и предполагайте, что вся задействованная приложением память должна быть освобождена. Хотя это и преувеличение действительной проблемы, потому что только в очень редких случаях действительно требуется освободить всю память, однако это становится существенным при наличии у переменных и расширяемых свойств потенциальной склонности к утечкам.

7.5. Оптимизируем «тяжелые» JavaScript-вычисления

Наиболее существенным препятствием для выполнения в веб-браузере «тяжелых» вычислений является тот факт, что весь интерфейс пользователя в браузере останавливается и ждет окончания исполнения JavaScript-кода. Это означает, что ни при каких условиях нельзя допускать того, чтобы для завершения работы скрипта требовалось более 300 мс (а лучше, если гораздо меньше). Нарушение этого правила неминуемо ведет к плохому восприятию ресурса пользователем.

К тому же в веб-браузерах у JavaScript-процесса имеется ограниченное время для завершения своего выполнения (это может быть фиксированное число в случае браузеров на движке Mozilla или какое-либо другое ограничение, например максимальное число элементарных операций в случае Internet Explorer). Если скрипт выполняется слишком долго, то пользователю выводится диалоговое окно, в котором запрашивается, нужно ли прервать скрипт.

Оптимизируем вычисления

Google Gears ( http://gears.google.com/ ) обеспечивает выполнение напряженных вычислений без двух вышеоговоренных ограничений. Однако в общем случае нельзя полагаться на наличие Gears (в будущем было бы замечательно, чтобы решение по типу Gears WorkerPool API стало частью стандартного API браузеров).

К счастью, у глобального объекта есть метод setTimeout, который позволяет выполнять определенный код с задержкой, давая тем самым браузеру возможность обработать события и обновить интерфейс пользователя. Это сработает даже в том случае, если задержка для setTimeout выставлена в 0, что позволяет разбить долгоиграющий процесс на множество небольших частей. Общий шаблон для обеспечения такой функциональности можно представить в следующем виде: