Javascript как найти утечку памяти

Обновлено: 01.07.2024

Я создал очень простой контрольный пример, который создает представление Backbone, присоединяет обработчик к событию и создает экземпляр пользовательского класса. Я полагаю, что при нажатии кнопки «Удалить» в этом примере все будет очищено и не должно быть утечек памяти.

Однако мне неясно, как использовать профилировщик Google Chrome, чтобы убедиться, что это действительно так. На снимке профилировщика кучи есть несколько миллиардов вещей, и я понятия не имею, как декодировать, что хорошо / плохо. Обучающие программы, которые я видел до сих пор, либо просто говорят мне «использовать профилировщик снимков», либо дают мне очень подробный манифест о том, как работает весь профилировщик. Можно ли просто использовать профилировщик в качестве инструмента или мне действительно нужно понять, как все это было спроектировано?

РЕДАКТИРОВАТЬ: Учебники, подобные этим:

Представляют некоторые из более сильных материалов, из того, что я видел. Однако, помимо представления концепции 3 Snapshot Technique , я считаю, что они предлагают очень мало с точки зрения практических знаний (для начинающего, как я). Учебное пособие «Использование DevTools» не работает на реальном примере, поэтому его расплывчатое и общее концептуальное описание вещей не слишком полезно. Что касается примера «Gmail»:

  • Изучите пути утечки предметов в нижней половине панели «Профили».

  • Если сайт размещения не может быть легко выведен (например, слушатели событий):

  • Инструментарий конструктора сохраняющего объекта через консоль JS для сохранения трассировки стека для выделений

  • Используя Закрытие? Включите соответствующий существующий флаг (например, goog.events.Listener.ENABLE_MONITORING), чтобы установить свойство creationStack во время построения.

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

Некоторые из этих более конкретных вопросов были подняты в ответе @Jonathan Naguin ниже.

Найти и обезвредить: утечки памяти в Node.js

Если вы ломаете голову над тем, почему ваше приложение JavaScript преподносит неприятные сюрпризы в виде сильного торможения, низкой производительности, продолжительных задержек или частых сбоев, и все ваши старательные попытки выяснить причину ни к чему не приводят, то, скорее всего, в вашем коде происходят утечки памяти.

Это довольно распространенная проблема. Дело в том, что многие разработчики пренебрегают управлением памятью из-за неправильных представлений об ее автоматическом выделении и освобождении в современных высокоуровневых языках программирования, например JavaScript.

Своевременно же не решенный вопрос ее утечек может обернуться резким снижением производительности приложения вплоть до невозможности его нормального использования.

Пространство Интернета постоянно пополняется сложными жаргонизмами, в которых весьма непросто разобраться. Данная статья будет построена по другому принципу — просто и понятно. Вы узнаете о том, что такое утечки памяти и каковы их причины; как их легко обнаружить и диагностировать с помощью Chrome Developer Tools.

Начнем с того, что ключ к их пониманию лежит в понимании принципов управления памятью в Node.js. А это, в свою очередь, означает, что мы должны разобраться, как это управление осуществляется движком V8, используемым Node.js для JavaScript.

Вкратце напомню вам структуру памяти в V8.

Главным образом она делится на две области: стек (stack) и кучу (heap).

1.Стек — это область памяти, в которой хранятся статические данные, включающие фреймы методов/функций, примитивные значения и указатели на объекты. Он управляется ОС.

2.Куча — это самая большая область памяти, в которойV8хранит объекты или динамические данные.Здесь же происходит сборка мусора.

Цитируя Дип К Сасидхаран, разработчика и одного из авторов книги “Развитие полного стека с JHipster”, отметим, что

“V8 управляет памятью кучи с помощью сборки мусора. Проще говоря, он освобождает память, используемую висячими объектами, т.е. объектами, на которые нет прямых или косвенных (через ссылку в другом объекте) ссылок из стека, для освобождения пространства с целью создания нового объекта.

Сборщик мусора в V8 отвечает за восстановление неиспользуемой памяти для повторного ее применения в процессе работы движка. Сборка мусора происходит по поколениям (объекты в куче распределяются по группам в зависимости от времени жизни и удаляются на разных этапах). В V8 существуют два этапа и три разных алгоритма сборки мусора”.

Что такое утечки памяти?

Простыми словами, утечка памяти — это не что иное, как фрагмент памяти в куче, который больше не нужен приложению, но который не был возвращен оперативной системе сборщиком мусора.

И вот мы имеем неиспользуемый фрагмент памяти. Со временем результатом накопления таких фрагментов станет ваше приложение, сигнализирующее о нехватке памяти для работы или даже ОС, требующая места для выделения памяти. А все это вместе чревато торможениями и/или выходом приложения или даже ОС из строя.

В чем же причина утечек памяти в JS?

Автоматическое управление памятью, подразумевающее сборку мусора в V8, предназначено для предотвращения ее утечек. Например, циклические ссылки больше не вызывают беспокойства, но все-таки могут возникать из-за нежелательных ссылок в куче или по каким-либо другим причинам.

Рассмотрим несколько самых распространенных причин:

1.Глобальные переменные. Поскольку на них в JavaScript ссылается корневой узел (глобальный объект window или ключевое слово this ), то они никогда не подвергаются сборке мусора в течение всего жизненного цикла приложения и будут занимать память до тех пор, пока оно выполняется. И это относится ко всем объектам, на которые ссылаются глобальные переменные, а также к их потомкам. Наличие большого графа объектов со ссылками из корня может привести к утечке памяти.

2. Множественные ссылки. Ситуации, когда на один и тот же объект ссылаются несколько объектов, также могут вызвать утечку памяти при условии, что одна из ссылок становится висячей.

3. Замыкания. ЗамыканияJavaScript обладают превосходным свойством запоминать окружающий их контекст, вследствие чего ссылки на крупные объекты кучи, используемые в них, сохраняются дольше, чем требуется.

4. Таймеры и события. Использование setTimeout , setInterval , Observers и слушателей событий может вызвать утечки памяти в том случае, если ссылки на объекты кучи хранятся в их обратных вызовах без правильной обработки.

Лучшие практики для предотвращения утечек памяти

Теперь, когда мы разобрались в причинах возникновения утечек памяти, давайте посмотрим, как их избежать и какие практики взять на вооружение для эффективного использования памяти.

Сокращение использования глобальных переменных

Поскольку глобальные переменные не подвергаются сборке мусора, то лучше всего убедиться, что вы не злоупотребляете их использованием. Ниже речь пойдет о том, как это сделать.

Обходимся без случайных глобальных переменных

Когда вы присваиваете значение необъявленной переменной, JavaScript по умолчанию определяет ее как глобальную. Это может произойти по ошибке и привести к утечке памяти. Подобное может случиться в результате присвоения переменной к this .

Во избежание таких сюрпризов, всегда пишите код JavaScript в режиме strict и используйте нотацию 'use strict'; в начале файла JS.

В режиме strict выше приведенный пример приведет к ошибке. Однако при использовании ES модулей и компиляторов, таких как TypeScript или Babel, нет необходимости включать данный режим, так как он будет задействован по умолчанию. В последних версиях Node.js вы можете активировать режим strict глобально, сопроводив выполнение команды node флагом --use_strict .

И наконец, помните, что не следует привязывать глобальное this к функциям, использующим методы bind или call , так как это лишает использование режима strict всякого смысла.

Умеренное использование глобальной области видимости

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

  1. Минимизируйте использование глобальной области видимости. Вместо этого рассмотрите возможность применения локальной области внутри функций, так как они будут удалены в процессе сборки мусора для освобождения памяти. Если вам вынужденно приходится прибегать к использованию глобальной переменной, то задайте ей значение null , когда в ней уже не будет необходимости.
  2. Используйте глобальные переменные только для констант, кэша и переиспользуемых объектов-одиночек. Не стоит применять их в целях избежания передачи значений в коде. Для обмена данными между функциями и классами передавайте значения в качестве параметров или атрибутов объектов.
  3. Не храните крупные объекты в глобальной области видимости. Если же вам приходится это делать, то не забудьте определить их как null, когда они более не нужны. В отношении объектов кэша рекомендуется установить обработчик для периодической очистки, препятствующий их неограниченному росту.

Эффективное использование стека

Максимально возможное использование переменных стека способствует эффективной и производительной работе памяти, так как доступ к стеку происходит гораздо быстрее, чем к куче. Это также позволяет избежать случайных утечек памяти.

Конечно, использование исключительно статических данных непрактично. В реальных приложениях нам приходится работать со многими объектами и динамическими данными. Но мы можем оптимизировать применение переменных стека при помощи ряда приемов:

1.Избегайте использования ссылок переменных стека на объекты кучи по мере возможности и не храните неиспользуемые переменные;

2.Деструктуризируйте и используйте только необходимые поля объектов или массивов вместо того, чтобы целиком передавать их функциям, замыканиям, таймерам и обработчикам событий. Тогда ссылки на объекты уже не будут оставаться в замыканиях. Передаваемые поля могут быть в основном примитивами, которые будут храниться в стеке.

Эффективное использование кучи

В любом реальном приложении мы так или иначе будем использовать кучу, но с помощью следующих рекомендаций можно сделать работу с ней более эффективной:

1.По возможности копируйте объекты вместо того, чтобы передавать ссылки. Их передача возможна только в том случае, если объект крупный или операция копирования требует больших затрат.

2.По максимуму обходитесь без мутаций объекта. Вместо этого для их копирования используйте распространение объекта или Object.assign .

3.Вместо создания множественных ссылок на объект просто его скопируйте.

4.Используйте переменные с коротким жизненным циклом.

5.Старайтесь не создавать огромные деревья объектов. Если же это неизбежно, то обеспечьте им короткий жизненный цикл в локальной области видимости.

Грамотное использование замыканий, таймеров и обработчиков событий

Как уже было отмечено ранее, замыкания, таймеры и обработчики событий — это те области, где могут произойти утечки памяти. Начнем с замыканий, как наиболее часто встречающихся в коде JavaScript. Посмотрим на следующий код команды Meteor, который приводит к утечке памяти, так как переменная longStr не подлежит удалению и увеличивает объём занимаемой памяти.

Выше обозначенный код создает несколько замыканий, которые удерживают ссылки на объекты. В этом случае устранение утечки памяти возможно путем определения originalThing как null в конце выполнения функции replaceThing . Подобных ситуаций тоже можно избежать, создавая копии объектов и соблюдая выше описанный немутабельный подход.

Когда дело касается таймеров, всегда передавайте копии объектов и обходитесь без мутаций. По окончании работы таймеров проводите их очистку с помощью методов clearTimeout и clearInterval .

Тоже самое относится к слушателям событий и наблюдателям. Как только они выполнили свою задачу, очистите их. Не оставляйте слушателей в постоянно работающем состоянии, особенно если они удерживают ссылки на объекты из родительской области видимости.

Заключение

В настоящее время утечки памяти в JavaScript не являются такой уж большой проблемой, как бывало раньше, благодаря эволюции движков JS и улучшениям в языке. Но если мы не будем внимательны, то они по-прежнему будут происходить и приводить к снижению производительности и сбоям в работе приложения/ОС.

Во-первых, чтобы убедиться, что наш код не приводит к утечкам памяти в приложении Node.js, нужно разобраться в принципах ее управления движком V8. Во-вторых, важно понять, что послужило их причиной.

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

Я боролся с профилировщиком кучи в chrome, но это очень запутанно. Особенно, если внутри есть минимизированные библиотеки. Но даже виды куполов, элементы, которые не могут быть удалены, очень неясны.

есть ли советы, как использовать дамп кучи в chrome, чтобы найти код JS, который приводит к утечкам памяти, код, который не может быть очищен GC. и как найти элементы, которые возятся, даже если они удалены из dom?

другими словами, как правильно читать дамп кучи в chrome? Вид Доминаторов? Сравнение?

Google open sourced инструмент для этой цели,leak-finder-for-javascript. Они также предложили метод, так называемый три снимка техника (Также см. краткое описание в в этой статье).

Примечание: jsleakcheck больше не поддерживается! Он работал против неофициального и нестабильного протокола Dev Tools для получения снимков кучи. Протокол разрабатывается, и он недостаточно стабилен, чтобы я может поддерживать работу jsleakcheck с различными версиями Chrome. В кроме того, инструмент совместимости более низкого уровня, remote_inspector_client.py, который jsleakcheck общался с помощью Dev Tools был удален.

в инструментах разработчика Chrome есть вкладка Timeline-Memory:

enter image description here

мы можем наблюдать за памятью, занимаемой им.

есть также профили-память, где мы можем сделать снимок и посмотреть, что внутри. Снимки можно сравнить друг с другом:

enter image description here

большую часть времени он ничего вам не говорит. Но, по крайней мере, вы можете видеть, какие объекты накапливаются, и, вероятно, структуру утечка.

другой способ-использовать 'Task Manager' вот статья об этом:

вот чтобы найти утечки памяти в javascript с помощью недавнего браузера Chrome:

  1. пресс F12 открыть инструменты разработчика и перейти на Вкладка Памяти.

Chrome Developer Tools - Memory Tab

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

выполните действие (например, открытие диалога), которое вы хотите проверить на наличие утечек памяти один раз, чтобы можно было загрузить потенциальные глобальные службы. Это предотвращает появление этих объектов, которые намеренно сохраняются как утечки.

Теперь выберите Запись Временной Шкалы Распределения и нажать старт. Повторить действие, которое вы хотите проверить на наличие утечек в несколько раз. Например, откройте диалог, закройте его и повторите. При этом Chrome рисует временную шкалу с частично серыми или синими полосами. Обычно вы видите панель каждый раз, когда вы выполняете действие на своей странице. когда полоса из нескольких предыдущих итераций действия остается частично синей, это обычно означает, что происходит утечка памяти. Синяя часть панели представляет память, которая была выделена в это время и еще не была освобождена снова. остановите запись, нажав красная точка в левом верхнем углу инструментов разработчика.

Memory timeline

  1. когда вы видите потенциальные утечки, вы должны проверить эту часть временной шкалы, чтобы найти источник. Выберите часть временной шкалы, которая представляет собой несколько повторений ваших действий в прошлом. И хром покажет список типов объектов, которые все еще присутствуют в памяти. The сохранил размере столбец дает вам впечатление, сколько памяти все еще используется. Перейдите к одному из типов объектов и выберите объект. Если вы это сделаете, список слуг появится ниже.

List of retainers

в списке ретейнеров отображаются "родительские" объекты, ссылающиеся на выбранный объект. Теперь вам нужно посмотреть на фиксаторы и ваш код, чтобы понять, почему память не была выпущена. Например, на изображении вы видите объект типа scope. Во второй строке говорится, что область действия - " контекст in initFormat ()". Проблема в том, что initFormat был прослушиватель событий, который не был снят после остался диалог.

после исправления кода проверьте, решена ли проблема. Обновите страницу и повторите шаги от 3 до 6. Если вы никогда не проверяли утечки памяти, прежде чем это не маловероятно, что вы найдете несколько проблем.

Дополнительные советы:

  • иногда есть кэши, которые сохранить часть памяти. Обычно их можно игнорировать.
  • когда вы видите HTMLDivElement или другие элементы DOM в списке типов объектов имеют вид. Если объекты в этом списке выделены красным цветом, это означает, что они больше не присутствуют на Вашей странице. Это означает, что они должны быть ссылка где-то в коде. Возможно, вы забыли отключить прослушиватель событий.
  • читайте об утечках памяти в целом, так вы можете определить их более быстро в вашем код.

Я нашел эту статью очень проницательны:

Он широко охватывает инструменты разработчика chrome и очень хорошо объясняет, как работать, когда ваше приложение, похоже, имеет проблемы с памятью.

Если вы разрабатываете клиентские повторно используемые объекты сценариев, рано или поздно вы обнаружите утечки памяти. Скорее всего, ваш браузер будет сосать память, как губка, и вы вряд ли сможете найти причину, по которой отзывчивость вашей прекрасной навигации DHTML сильно уменьшается после посещения нескольких страниц на вашем сайте.

разработчик Microsoft Джастинг Роджерс описал шаблоны утечки IE в своей превосходной статье.

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

почему утечка памяти?

проблема утечки памяти не ограничивается только Internet Explorer. Почти любой браузер (включая, но не ограничиваясь Mozilla, Netscape и Opera) будет утечка памяти, если вы обеспечите адекватные условия (и это не так сложно сделать, как мы вскоре увидим). Но (по моему скромному мнению, ymmv и т. д.) Internet Explorer является королем утечки.

Не поймите меня неправильно. Я не принадлежу к толпе, кричащей "Эй, IE имеет утечки памяти, проверьте этот новый инструмент [link-to-tool] и убедитесь сами". Давайте обсудим, как дерьмовый Internet Explorer и скрыть все недостатки в других браузерах".

каждому браузеру имеет свои сильные и слабые стороны. Например, Mozilla потребляет слишком много памяти при начальной загрузке, это не хорошо в строковых и массивных операциях; Opera может произойти сбой, если вы напишете смехотворно сложный скрипт DHTML, который путает его механизм рендеринга.

хотя мы сосредоточимся на ситуациях утечки памяти в Internet Explorer, это обсуждение одинаково применимо и к другим браузерам.

Lorem ipsum dolor

Утечка в JS контролируется сборщиком мусора, который периодически «прочесывает» работу приложения и определяет , какие фрагменты памяти задействованы приложением, а какие нет. Незадействованную память он может «удалить», возвратив ее операционной системе. Однако проблема заключается в том, что сборщик мусора не способен определить, будет ли неиспользуемая память нужна приложению в ближайшем будущем. Есть вероятность, что память не нужно возвращать, так как она будет использоваться приложением. Такие вещи способен различать только разработчик. Поэтому работа сборщика мусора не лишена проблем.

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

Чтобы утечка памяти в JS не приносила вам проблем, важно знать , из-за чего она может образоваться.

Утечка в JS: распространенные виды

  • случайной глобальной переменной;

  • забытого таймера;

  • слушателей событий;

  • замыкания.

Случайная глобальная переменная

Случайные глобальные переменный получаются в JavaScript из-за его «нестрогости». Из-за этого необъявленная переменная может быть обработана. Ее обработка приводит к созданию глобальной переменной. Смотрим код:

function MyFunction(arg)

bar = "будет скрытой глобальной переменной";

>

Если нужно было, чтобы переменная « bar » была только внутри функции « MyFunction » , тогда нужно было объявить ее через « var » .

Также глобальная переменная может быть вызвана, если неправильно применить слово « this » . Смотрим код:

function MyFunction ()

this.bar = "это потенциально глобальная переменная";

>

Чтобы ограничить себя от подобных ошибок, которые могут быть просто незамеченными, но в последстви и вы зва ть много проблем, нужно использовать выражение « use strict » в начале JS-скриптов. Это выражение включает строгий режим JavaScript, при котором возникновение случайных глобальных переменных исключено.

Забытый таймер

  • осознавать , на какие объекты ссылается обратный вызов таймера;

  • вовремя отменять вызов таймера методами « clearTim eout» и « clearInterval » .

Слушатель событий

  • не удалить его методом « removeEventListener() » ;

  • не удалить элемент, связанный со слушателем.

Замыкания

  • определить вероятное замыкание со всеми объектами, которые оно удерживает, а также когда оно было создано;

  • определить вероятную продолжительность замыкания.

Утечка памяти в С/С++

Утечка С: решение

  • отладчик С/С++;

  • и отладочные функции библиотеки CTR.

Заключение

Утечка памяти в С, JavaScript или другом языке программирования всегда является причиной дополнительных проблем с программой. Писать без утечек пока не может никто. Но находить и устранять их можно. Где-то благодаря внимательност и программиста и « строг ому режим у» языка, а где-то при помощи дополнительного инструмента.

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

Читайте также: