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();
Посмотрим, как выполняется код. Сначала объявляются две функции, называемыеfn1
а такжеfn2
,когдаfn1
При вызове введитеfn1
среда, откроет часть памяти для хранения объектов{name: 'hanzichi', age: 10}
, а когда вызов завершится и среда fn1 выйдет, блок памяти будет автоматически освобожден сборщиком мусора в движке JS;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, это означает, что нет возможности получить доступ к этому значению, поэтому занимаемое им пространство памяти может быть восстановлено. Таким образом, при следующем запуске сборщика мусора он освободит память, занятую нулевыми значениями, на которые ссылаются.
function test() {
var a = {}; // a指向对象的引用次数为1
var b = a; // a指向对象的引用次数加1,为2
var c = a; // a指向对象的引用次数再加1,为3
var b = {}; // a指向对象的引用次数减1,为2
}
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;
Этот пример в элементе DOMelement
с нативным js-объектомmyObject
Между ними создается циклическая ссылка. Среди них переменнаяmyObject
имеет атрибутe
направлениеelement
объект; переменнаяelement
также имеет свойствоo
указывая назадmyObject
. Из-за этой циклической ссылки, даже если модель DOM в примере будет удалена со страницы, она никогда не будет переработана.
Возьмите каштан:
- Желтый означает, что на него напрямую ссылаются переменные js в памяти.
- Красный означает, что переменные js косвенно ссылаются на него.Как показано на рисунке выше, на refB косвенно ссылается refA, поэтому даже если переменная refB очищена, она не будет переработана.
- дочерний элемент refB из-за
parentNode
Косвенная ссылка на , пока она не удалена, все ее родительские элементы (красная часть на рисунке) не будут удалены.
другой пример:
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){};
};
Этот код выглядит нормально, но ссылки objdocument.getElementById('element')
,а такжеdocument.getElementById('element')
изonclick
Метод будет ссылаться на переменные во внешнем окружении, естественно в том числе и на obj, не сильно ли это скрыто? (В более новых браузерах при удалении Node событие на нем убрано, но в старых браузерах, особенно IE, будет этот баг)
Решение:
Самый простой способ — вручную удалить циклическую ссылку самостоятельно, например, функцию только что.
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. Дефекты ГК
Как и в других языках, стратегия сборщика мусора в JS не может избежать проблемы: во время сборщика мусора он перестает реагировать на другие операции из соображений безопасности. 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
функция; - Мониторинг событий других объектов BOM, таких как функция включения экземпляра веб-сокета;
- Избегайте ненужных ссылок на функции;
- При использовании
render
функция, позволяющая избежать привязки событий DOM/BOM в тегах HTML;
5.3 Как с этим бороться
- если в
mounted/created
Хук использует JS для привязки событий в объекте DOM/BOM, который необходимоbeforeDestroy
Выполните соответствующую обработку отвязки в ; - если в
mounted/created
В хуке используется инициализация сторонней библиотеки, которую нужноbeforeDestroy
Выполните соответствующую обработку уничтожения посередине (обычно не используется, потому что во многих случаях она является непосредственно глобальной).Vue.use
); - Если используется компонент
setInterval
, должен быть вbeforeDestroy
Выполните соответствующую обработку уничтожения;
5.4 Обработка addEventListener в компоненте vue
передачаaddEventListener
После добавления прослушивателей событийbeforeDestroy
вызыватьremoveEventListener
Удалите соответствующий прослушиватель событий. Чтобы точно удалить прослушиватели, старайтесь не использовать анонимные функции или привязки существующих функций напрямую в качестве прослушивателей событий.
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
устанавливается равным нулю, ноb
Контекст закрытия bind в методе self связанa
, так что хотяa
освободить, ноb/b.func
Без освобождения само замыкание сохраняется и остаетсяa
цитаты.
Большинство сообщений в Интернете имеют разную глубину и даже некоторые несоответствия. Следующие статьи являются кратким изложением процесса обучения. Если вы найдете какие-либо ошибки, пожалуйста, оставьте сообщение, чтобы указать ~
Ссылаться на:
Рекомендуемое чтение:
PS: Всех приглашаю обратить внимание на мой публичный аккаунт [Front End Afternoon Tea], давайте работать вместе~
Кроме того, вы можете присоединиться к группе WeChat «Front-end Afternoon Tea Exchange Group», нажмите и удерживайте, чтобы определить QR-код ниже, чтобы добавить меня в друзья, обратите вниманиеДобавить группу, я заберу тебя в группу~