что такое утечка памяти
Утечка памяти может быть определена как блок памяти, который больше не используется или не нужен программе, но по какой-то причине не освобождается и остается без необходимости занятым. Создание объектов и переменных в коде потребляет память,Но в javaScript есть собственный механизм рециркуляции памяти, который может определить те переменные, которые больше не нужны, и очистить их. Но когда в вашем коде есть логические изъяны, вы думаете, что вам это не нужно, а ссылки в программе все равно есть, пространство, занимаемое программой, не освобождается должным образом после запуска, что приводит к непрерывному занятию памяти, и чем дольше время работы, тем больше она занимает, что сопровождается низкой производительностью, высокой задержкой и частыми сбоями. Прежде чем погрузиться в утечку памяти, нам нужно знать несколько вещей:
- жизненный цикл памяти
- система управления памятью
- Алгоритм сборки мусора
жизненный цикл памяти
Независимо от языка программирования жизненный цикл памяти в основном одинаков: изображение на обложке статьи
- Выделите необходимую память
- Использовать выделенную память (чтение, запись)
- Отпустите его, когда он не нужен\возвращается
Вторая часть является явной на всех языках. Части 1 и 3 являются явными в языках низкого уровня, но в языках высокого уровня, таких как JavaScript, большинство из них являются неявными.
Система управления памятью: ручная или автоматическая
Разные языки по-разному обрабатывают свою память.
- Языки низкого уровня. Языки низкого уровня, такие как C, обычно имеют низкоуровневые интерфейсы управления памятью, такие как malloc() и free().
- Языки высокого уровня: JavaScript автоматически выделяет память при создании переменных (объектов, строк и т. д.) и «автоматически» освобождает их, когда они не используются. Процесс освобождения называется сборкой мусора. Эта «автоматика» является источником путаницы и дает разработчикам JavaScript (и других языков высокого уровня) ложное ощущение, что они не могут заботиться об управлении памятью.
Алгоритм сборки мусора
Механизм сборки мусора работает, периодически проверяя, какая ранее выделенная память "все еще нужно"(×) Механизм сборки мусора работает, периодически проверяя, какая ранее выделенная память "память по-прежнему доступна для других частей программы"(√).
А? . . .
Это ключ к пониманию сборки мусора, только разработчик знает, «нужна» ли эта часть памяти для будущего использования, но могут быть алгоритмы для определения недоступной памяти и пометки ее обратно в операционную систему.
- подсчет ссылок
- пометить как очищенный
1. Подсчет ссылок
Это самый простой алгоритм сборки мусора, если на объект нет ссылок (ноль ссылок), объект будет утилизирован механизмом сборки мусора. (пример из MDN выше)
var o = {
a: {
b:2
}
};
// 两个对象被创建,一个作为另一个的属性被引用,
另一个被分配给变量o
// 很显然,没有一个可以被垃圾收集
var o2 = o; // o2变量是第二个对“这个对象”的引用
o = 1; // 现在,“这个对象”的原始引用o被o2替换了
var oa = o2.a; // 引用“这个对象”的a属性
// 现在,“这个对象”有两个引用了,一个是o2,一个是oa
o2 = "yo"; // 最初的对象现在已经是零引用了
// 他可以被垃圾回收了
// 然而它的属性a的对象还在被oa引用,所以还不能回收
oa = null; // a属性的那个对象现在也是零引用了
// 它可以被垃圾回收了
Недостаток: в случае циклов алгоритм подсчета ссылок имеет существенные ограничения.
function foo(){
var obj1 = {};
var obj2 = {};
obj1.x = obj2 ; // obj1引用obj2
obj2.x = obj1 ; // obj2引用obj1
return true ;
}
foo();
2. Удаление тегов
Алгоритм состоит из следующих шагов:
- Сборщик мусора создает список «корней». Корни обычно являются ссылками на глобальные переменные в коде. В JavaScript объект «окно» является глобальной переменной и рассматривается как корень. Объект окна всегда существует, поэтому сборщик мусора может проверить, существует ли он и все его дочерние элементы (т. е. не мусор);
- Все корни проверяются и помечаются как активные (т.е. не мусорные). Все дочерние объекты также проверяются рекурсивно. Все объекты, начиная с root, не считаются мусором, если они достижимы.
- Вся непомеченная память считается мусором, и теперь сборщик может освободить память и вернуть ее операционной системе.
нет фото ( )
Проблема циклических ссылок решена, хотя этот алгоритм постоянно совершенствуется, суть этих улучшений в js сборке мусора (генерация/инкрементальная/конкурентная сборка мусора) осталась прежней:Доступная память помечается, а остальная часть удаляется сборщиком мусора.
Недостаток: выполнение программы приостанавливается на время работы алгоритма.
Механизм сбора мусора непредсказуем
Хотя механизм сборки мусора красив и удобен, это компромисс, причина в том, что мы не можем быть уверены, когда будет выполнена сборка, только разработчик может определить, можно ли вернуть блок памяти в операционную систему. . Нежелательные ссылки относятся к тому факту, что разработчик знает, что ссылка на память больше не нужна, но когда мы пишем программу, она по какой-то причине остается в активном корневом дереве.
как избежать
Основной причиной утечек языка со сборщиком мусора являются нежелательные ссылки. Чтобы понять, что такое нежелательная ссылка, сначала нам нужно понять, как сборщик мусора определяет, можно ли получить доступ к блоку памяти.
следовательно,Чтобы понять, какие утечки в JavaScript являются наиболее распространенными, нам нужно знать, как часто забываются ссылки..
Четыре распространенные утечки памяти
1. Глобальные переменные
При ссылке на необъявленную переменную в нестрогом режиме в глобальном объекте создается новая переменная. В браузерах глобальным объектом будет окно, что означает
function foo(arg){
bar =“some text”; // bar将泄漏到全局.
}
Почему он не может просочиться в глобальную, мы обычно определяем глобальные переменные!!!
**Причина**: Глобальные переменные по определению не могут быть собраны механизмом сборки мусора.Особое внимание следует уделить глобальным переменным, используемым для временного хранения и обработки больших объемов информации. Если вы должны использовать глобальную переменную для хранения данных, обязательно укажите ее как нулевую или переназначьте ее, когда закончите. ** Обходной путь **: Строгий режим
2. забытые таймеры и обратные вызовы
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if(node) {
node.innerHTML = JSON.stringify(someResource));
// 定时器也没有清除
}
// node、someResource 存储了大量数据 无法回收
}, 1000);
причина: Таймер, связанный с узлом или данными, больше не нужен, объект узла можно удалить, и вся функция обратного вызова больше не нужна. Однако функция обратного вызова таймера по-прежнему не перезапускается (таймер будет перезапущен только после остановки). В то же время, если какой-то ресурс хранит большой объем данных, он не может быть переработан.
Решение: Вручную очистить таймер, когда таймер закончит свою работу.
3. DOM-ссылки
var refA = document.getElementById('refA');
document.body.removeChild(refA); // dom删除了
console.log(refA, "refA"); // 但是还存在引用
能console出整个div 没有被回收
причина: Ссылка на узел DOM сохраняется, что приводит к отсутствию повторного использования сборщика мусора.
Решение:refA = ноль;
Уведомление: Также рассмотрите вопрос о ссылках в дереве DOM или дочерних узлах. Предположим, ваш код JavaScript содержит ссылку на
4. Закрытие
Уведомление
Уведомление
Примечание: Само закрытие не является неправильным, оно не приведет к утечке памяти, оно вызвано использованием ошибок.
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
var unused = function () {
if (originalThing)
console.log("hi");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log(someMessage);
}
};
};
setInterval(replaceThing, 1000);
Это ужасный код, каждый раз, когда replaceThing вызывается, theThing получает новый объект, содержащий большой массив и новое замыкание (someMethod). Между тем переменная unused является замыканием, которое ссылается на originalThing (предыдущая replaceThing, в свою очередь, называлась theThing). Смущенный? Самое главное, что после создания области замыкания они имеют одну и ту же родительскую область, и эта область является общей. someMethod доступен через theThing , someMethod разделяет область закрытия с unused , хотя unused никогда не используется, исходная вещь, на которую он ссылается, заставляет его оставаться в памяти (предотвращая его повторное использование). Когда этот код запускается повторно, вы увидите, что использование памяти продолжает расти, а сборщик мусора (GC) не может уменьшить использование памяти. По сути, был создан связанный список замыканий, и каждая область замыкания содержит косвенную ссылку на большой массив, вызывая серьезную утечку памяти.
решить: Удалите функцию unuserd или добавьте originlThing = null в последнюю строку функции replaceThing.
Ссылаться на:
4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them
How JavaScript works: memory management + how to handle 4 common memory leaks