1. Введение
Javascript браузера имеет механизм автоматической сборки мусора (GC: Garbage Collecation), то есть среда выполнения отвечает за управление памятью, используемой во время выполнения кода. Принцип таков:Сборщик мусора будет периодически (периодически) узнавать те переменные, которые больше не используются, а затем освобождать их память. Однако этот процесс не выполняется в режиме реального времени, поскольку накладные расходы относительно велики, а сборщик мусора перестает реагировать на другие операции, поэтому сборщик мусора будет выполняться периодически через равные промежутки времени.
Переменные, которые больше не используются, также являются переменными, чей жизненный цикл заканчивается.Конечно, они могут быть только локальными переменными.Жизненный цикл глобальных переменных не закончится, пока браузер не выгрузит страницу. Локальные переменные существуют только во время выполнения функции, и во время этого процесса в стеке или куче выделяется соответствующее пространство для локальных переменных для хранения их значений, а затем эти переменные используются в функции до конца функции, и закрытие. Из-за внутренней функции в пакете внешняя функция не считается концом.
Поясним код:
function fn1() {
var obj = {name: 'hanzichi', age: 10};
}
function fn2() {
var obj = {name:'hanzichi', age: 10};
return obj;
}
var a = fn1();
var b = fn2();
Смотрим, как выполняется код. Две первые определенные функции и Fn2 называются fn1, при вызове fn1, fn1 в среде открывается объект памяти, хранящийся {name: 'hanzichi', age: 10}, и когда вызов завершается, среда fn1, затем блок памяти автоматически освобождается сборщиком мусора js engine; вызывается процесс fn2, объект возвращается в глобальную переменную b точек, поэтому блок памяти не освобождается.
Здесь возникает проблема: какая переменная не используется? Следовательно, сборщик мусора должен отслеживать, какая переменная бесполезна, а для уже не используемой переменной она помечается, чтобы восстановить ее память в будущем. Стратегия полезных переменных для маркеров может отличаться от реализации, обычно есть две реализации:пометить как очищенныйа такжеподсчет ссылок. Подсчет ссылок встречается реже, а подметание меток более распространено.
2. Удаление тегов
Наиболее часто используемый метод сборки мусора в js — пометить и очистить. Когда переменная входит в среду, например, переменная объявляется в функции, переменная помечается как «входящая в среду». Логически память, занимаемая переменными, входящими в среду, никогда не может быть освобождена, потому что они могут быть использованы, как только поток выполнения входит в соответствующую среду. И когда переменная покидает среду, она помечается как «выходящая из среды».
function test(){
var a = 10 ; //被标记 ,进入环境
var b = 20 ; //被标记 ,进入环境
}
test(); //执行完毕 之后 a、b又被标离开环境,被回收。
Сборщик мусора пометит все переменные, хранящиеся в памяти, при запуске (конечно, можно использовать любой метод маркировки). Затем он удаляет переменные в среде и теги (замыкания) переменных, на которые ссылаются переменные в среде. Переменные, отмеченные после этого, будут рассматриваться как переменные, готовые к удалению, поскольку переменные в среде больше не доступны. Наконец, сборщик мусора завершает очистку памяти, уничтожая отмеченные значения и освобождая место в памяти, которое они занимали. На данный момент все js-реализации IE9+, Firefox, Opera, Chrome и Safari используют стратегии сборки мусора с маркировкой и зачисткой или аналогичные стратегии, но интервалы сборки мусора отличаются друг от друга.
3. Подсчет ссылок
Смысл подсчета ссылок заключается в отслеживании количества ссылок на каждое значение. Когда переменная объявляется и ей присваивается значение ссылочного типа, количество ссылок на это значение равно 1. Если такое же значение присваивается другой переменной, количество ссылок на это значение увеличивается на 1. И наоборот, если переменная, содержащая ссылку на это значение, принимает другое значение, количество ссылок на это значение уменьшается на 1. Когда количество ссылок на это значение становится равным 0, это означает, что нет возможности получить доступ к этому значению, поэтому занимаемое им пространство памяти может быть восстановлено. Таким образом, когда сборщик мусора запустится в следующий раз, он освободит память, занятую значениями с 0 ссылками.
function test(){
var a = {} ; //a的引用次数为0
var b = a ; //a的引用次数加1,为1
var c =a; //a的引用次数再加1,为2
var b ={}; //a的引用次数减1,为1
}
Netscape Navigator3 был первым браузером, использовавшим стратегию подсчета ссылок, но вскоре он столкнулся с серьезной проблемой: циклическими ссылками. Циклическая ссылка означает, что объект A содержит указатель на объект B, а объект B также содержит ссылку на объект A.
function fn() {
var a = {};
var b = {};
a.pro = b;
b.pro = a;
}
fn();
Время ссылки приведенного выше кода a и b равно 2. После выполнения fn() оба объекта покинули среду.Нет проблем в методе очистки меток, но при стратегии подсчета ссылок, поскольку ссылки a и bКоличество раз не равно 0, поэтому память не будет освобождена сборщиком мусора.Если функция fn вызывается много раз, это вызовет утечку памяти. В IE7 и IE8 объем памяти резко увеличился.
Мы знаем, что некоторые объекты в IE не являются нативными объектами js. Например, объекты DOM и BOM с утечкой памяти реализованы в виде COM-объектов с использованием C++, а механизм сборки мусора COM-объектов использует стратегию подсчета ссылок. Таким образом, даже если js-движок IE примет стратегию удаления разметки, js-доступCOM-объекты по-прежнему основаны на стратегии подсчета ссылок.из. Другими словами, пока в IE задействованы COM-объекты, существует проблема с циклическими ссылками.
var element = document.getElementById("some_element");
var myObject = new Object();
myObject.e = element;
element.o = myObject;
В этом примере создается циклическая ссылка между элементом DOM (element) и собственным объектом js (myObject). Среди них переменная myObject имеет атрибут e, указывающий на объект элемента, а переменная element также имеет атрибут o, указывающий обратно на myObject. Из-за этой циклической ссылки, даже если модель DOM в примере будет удалена со страницы, она никогда не будет переработана.
Возьмите каштан:
- Желтый означает, что на него напрямую ссылаются переменные js в памяти.
- Красный означает, что переменные js косвенно ссылаются на него.Как показано на рисунке выше, на refB косвенно ссылается refA, поэтому даже если переменная refB очищена, она не будет переработана.
- дочерний элемент refB из-за
parentNode
Косвенная ссылка на , пока она не удалена, все ее родительские элементы (красная часть на рисунке) не будут удалены.
другой пример:
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){};
};
Этот код выглядит нормально, но obj ссылается на document.getElementById('element'), а метод onclick документа document.getElementById('element') ссылается на переменные во внешней среде, естественно включая obj, не слишком ли это скрыто? (В относительно новых браузерах при удалении Node событие на нем уберется, а вот в старых браузерах, особенно т.е., будет этот баг)
Решение:
Самый простой способ — вручную удалить циклическую ссылку самостоятельно, например, функцию только что.
myObject.element = null;
element.o = null;
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){};
obj=null;
};
Установка переменной в значение null означает разрыв связи между переменной и значением, на которое она ранее ссылалась. При следующем запуске сборщика мусора эти значения удаляются, а занимаемая ими память освобождается.
Следует отметить, что в IE9+ нет циклической ссылки, вызывающей утечку памяти Dom.Возможно, Microsoft оптимизировала ее или изменился метод утилизации Dom.
4. Управление памятью
4.1 Когда запускается сборка мусора?
Сборщик мусора запускается периодически, если выделено много памяти, работа по восстановлению будет очень трудоемкой, определение интервала сборки мусора становится проблемой, над которой стоит задуматься. Сборка мусора в IE6 выполняется в соответствии с объемом выделенной памяти.Когда в среде 256 переменных, 4096 объектов и 64 тыс. строк, сборщик мусора запускается для работы.Это выглядит очень научно.Он вызывается только один раз в время, а иногда и ненужное. Разве не хорошо вызывать его по требованию? Но если в окружении все время столько переменных и так далее, а сейчас скрипт такой сложный, это нормально, то в результате всегда работает сборщик мусора, поэтому браузер не может играть.
Microsoft внесла коррективы в IE7.Условие срабатывания больше не фиксированное, а динамически модифицируется.Исходное значение такое же, как в IE6.Если объем памяти, выделяемой сборщиком мусора, меньше 15% памяти, занимаемой программой, Это означает, что большая часть памяти используется. Она не может быть переработана. Установленное условие триггера сборки мусора слишком чувствительно. В это время удвоить состояние улицы. Если восстановленная память выше 85%, это означает, что большая часть память должна быть очищена давно.В это время верните условие триггера. Это заставляет сборку мусора работать намного эффективнее.
4.2 Разумная схема GC
1. Базовый план
Базовая схема GC движка Javascript (простой GC): пометить и очистить, то есть:
- Перебрать все доступные объекты.
- Перерабатывайте объекты, которые больше не доступны.
2. Дефекты ГК
Как и в других языках, стратегия GC в javascript не может избежать проблемы: во время GC он перестает реагировать на другие операции по соображениям безопасности. GC Javascript составляет 100 мс или более, что хорошо для общих приложений, но для игр JS анимация предъявляет более высокие требования к непрерывности, что вызывает проблемы. Это тот момент, когда новый движок необходимо оптимизировать: чтобы избежать длительной остановки, вызванной сборщиком мусора.
3. Стратегия оптимизации сборщика мусора
Дядя Дэвид в основном представил 2 схемы оптимизации, и это самые важные 2 схемы оптимизации:
-
Переработка поколений(Поколение ГК) Это согласуется с идеей стратегии повторного использования Java и также в основном используется V8. Цель состоит в том, чтобы провести различие между «временными» и «постоянными» объектами; освободить больше «временной» области (молодое поколение) и меньше области «постоянного поколения», уменьшить количество объектов, которые необходимо каждый раз проходить, и, таким образом, уменьшить каждый ГК отнимает много времени. Как показано на рисунке:
Здесь необходимо добавить, что для объекта постоянного поколения существуют дополнительные накладные расходы: перенос его из молодого поколения в постоянное поколение, и если на него есть ссылка, контрольную точку также необходимо изменить. Основное содержание здесь может относиться кГлубокое понимание Node.Введение в память очень подробно~ -
Инкрементный сборщик мусораИдея этого плана очень проста: «обрабатывайте понемногу за раз, в следующий раз обрабатывайте немного больше и так далее». Как показано на рисунке:
Это решение, хотя и занимает много времени, имеет много прерываний, что приводит к проблеме частого переключения контекста.
Поскольку каждая схема имеет свои применимые сценарии и недостатки, в практических приложениях схема будет выбираться в соответствии с реальной ситуацией.
Например: при низком отношении (объект/с) частота прерывания выполнения GC ниже, а простой GC ниже; если большое количество объектов «выживает» длительное время, преимущество обработка поколений невелика.
5. Проблема с утечкой памяти в vue
После переполнения памяти JS-программы определенное тело функции будет недействительным навсегда (в зависимости от того, к какой функции в данный момент выполняется JS-код), что обычно показывает, что программа внезапно зависает или работает ненормально.
В это время нам нужно проверить утечку памяти JS-программы, чтобы узнать, какие объекты не освободили занимаемую ими память. Обычно считается, что эти объекты выпущены разработчиком, но на самом деле на них по-прежнему ссылается замыкание или они помещаются в массив.
5.1 Точка утечки
- Утечка объекта DOM/BOM
- Существование ссылки на объект DOM/BOM в скрипте приводит к
- утечка объекта js
- Обычно вызывается замыканиями, такими как обратные вызовы обработки событий, что приводит к двунаправленным ссылкам между объектами DOM и объектами в сценариях, что является распространенной причиной утечек в настоящее время.
5.2 Проблемы с кодом
- в Доме
addEventLisner
Функции и прослушиватели производных событий, например, в Jquery.on
функция, экземпляр компонента vue$on
функция, функция инициализации в сторонней библиотеке - Мониторинг событий других объектов спецификации, таких как функция включения экземпляра веб-сокета
- Избегайте ненужных ссылок на функции
- При использовании
render
Функция, позволяющая избежать привязки событий DOM/BOM в тегах html.
5.3 Как с этим бороться
- если в
mounted/created
Событие в объекте DOM/BOM привязано к хуку, который необходимоbeforeDestroy
Выполните соответствующую обработку отвязки в - если в
mounted/created
В хуке используется инициализация сторонней библиотеки, которую нужноbeforeDestroy
Выполните соответствующую обработку уничтожения в - Если в компоненте используется таймер, его необходимо
beforeDestroy
Выполните соответствующую обработку уничтожения в - Не используйте выражения в шаблонах для привязки к конкретной функции-обработчику, эту логику нужно поместить в функцию-обработчик?
- если в
mounted/created
используется в крючках$on
, должен быть вbeforeDestroy
сделать соответствующую отвязку$off
иметь дело с - Некоторые компоненты могут протекать при использовании привязки событий в шаблонах, используйте
$on
Замена привязок в шаблонах
5.4 процесс сборки vue в addEventListener
created/mounted
Метод определения функции ответа на событие как экземпляра объекта в функции ловушки жизненного цикла с использованием функции => для привязки области действия.
После вызова addEventListener для добавления прослушивателей событий,beforeDestroy
Вызовите removeEventListener, чтобы удалить соответствующий прослушиватель событий. Обратите внимание, что ранее определенный метод функции ответа должен быть передан в качестве второго параметра.
Затем используйте delete, чтобы удалить определенный метод ответа из экземпляра объекта, или установите для свойства значение null/undefined.
Чтобы точно удалить прослушиватель, не используйте анонимные функции или привязки к существующим функциям напрямую в качестве прослушивателей событий.
mounted() {
const box = document.getElementById('time-line')
this.width = box.offsetWidth
this.resizefun = () => {
this.width = box.offsetWidth
}
window.addEventListener('resize', this.resizefun)
},
beforeDestroy() {
window.removeEventListener('resize', this.resizefun)
this.resizefun = null
}
5.5 Утечки памяти, вызванные шаблоном наблюдателя
При использовании режима наблюдателя в спа-приложении, если наблюдаемый метод зарегистрирован для наблюдателя и вовремя не удален при выходе из компонента, это может привести к повторной регистрации и утечке памяти;
Возьмите каштан:
при входе в компонентob.addListener("enter", _func)
, если оставить компонентbeforeDestroy
когда нетob.removeListener("enter", _func)
, это вызовет утечку памяти
Более подробная ссылка на каштан:Техасский Холдем Каштаны
5.6 Утечки памяти, вызванные привязкой контекста
иногда используетсяbind/apply/call
При использовании метода привязки контекста будет иметь место скрытая утечка памяти.
var ClassA = function(name) {
this.name = name
this.func = null
}
var a = new ClassA("a")
var b = new ClassA("b")
b.func = bind(function() {
console.log("I am " + this.name)
}, a)
b.func() // 输出: I am a
a = null // 释放a
//b = null; // 释放b
//b.func = null; // 释放b.func
function bind(func, self) { //模拟上下文绑定
return function() {
return func.apply(self)
}
}
Используйте chrome dev tool > memory > profiles, чтобы просмотреть количество экземпляров ClassA в памяти и обнаружить, что есть два экземпляра, a и b. Хотя для a установлено значение null, контекст замыкания self для bind в методе b привязан к a, поэтому, хотя a освобождается, b/b.func не освобождается, self замыкания всегда существует и сохраняет ссылку на a. .
Большинство сообщений в Интернете имеют разную глубину и даже некоторые несоответствия. Следующие статьи являются кратким изложением процесса обучения. Если вы найдете какие-либо ошибки, пожалуйста, оставьте сообщение, чтобы указать ~
Ссылаться на:
Рекомендуемое чтение:
PS: Всех приглашаю обратить внимание на мой публичный аккаунт [Front End Afternoon Tea], давайте работать вместе~
Кроме того, вы можете присоединиться к группе WeChat «Front-end Afternoon Tea Exchange Group», нажмите и удерживайте, чтобы определить QR-код ниже, чтобы добавить меня в друзья, обратите вниманиеДобавить группу, я заберу тебя в группу~