Статья для понимания сборки мусора двигателя V8

JavaScript

введение

Как самый популярный движок JavaScript в настоящее время, движок V8 привлек к себе всеобщее внимание с момента своего появления.Мы знаем, что JavaScript может эффективно работать в двух основных хост-средах браузеров и Nodejs, а также благодаря мощному V8, стоящему за ним. Движок его сопровождает и даже добивается доминирования Chrome в браузере. Я должен сказать, что двигатель V8 слишком много сделал для нас в погоне за максимальной производительностью и лучшим пользовательским интерфейсом, начиная с оригинальногоFull-codegenа такжеCrankshaftОбновление компилятора доIgnitionпереводчик иTurboFanМощная комбинация компиляторов, скрытых классов, встроенного кэширования иHotSpotБлагодаря ряду мощных стратегий оптимизации, таких как сбор горячего кода, движок V8 усердно работает над сокращением общего использования памяти и повышением производительности.

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

1. Зачем нужна сборка мусора?

Мы знаем, что в процессе выполнения кода JavaScript построчно в движке V8 при встрече с функцией для нее будет создана среда контекста выполнения функции (Context) и добавлена ​​в начало стека вызовов. (handleScope) ) содержит все переменные, объявленные в функции.При выполнении функции соответствующий контекст выполнения извлекается из вершины стека, область действия функции уничтожается, а все содержащиеся в ней переменные освобождаются. и автоматически перерабатывается. Только представьте, что если в процессе уничтожения переменные в этой области видимости не восстанавливаются, то есть память будет постоянно занята, то это неизбежно приведет к резкому увеличению памяти, что приведет к утечкам памяти и вызовет производительность программы падает или даже падает, поэтому память израсходована, после чего ее следует вернуть в операционную систему, чтобы обеспечить повторное использование памяти.

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

Однако, как высокоуровневый язык программирования, JavaScript не нуждается в ручном выделении и освобождении памяти, как в C или C++. Движок V8 уже помог нам автоматически выделять и управлять памятью, так что у нас есть больше энергии, чтобы сосредоточиться на сложной логике. на уровне бизнеса — это преимущество для наших фронтенд-разработчиков, но и проблемы, которые с этим связаны, тоже очевидны, т. е. нет необходимости вручную управлять памятью, процесс написания кода недостаточно строгий. легко вызвать утечку памяти (в конце концов, это чужая доброта к вам, как вы можете испытать ее, если вы за нее не заплатили?).

2. Предел памяти двигателя V8

Хотя двигатель V8 помогает нам добиться автоматического управления сборкой мусора, освобождая наши трудолюбивые руки, но использование памяти в двигателе V8 не безгранично. В частности, по умолчанию двигатель V864Под системой вы можете использовать только около ок.1.4GBпамять, в32Под системой вы можете использовать только около ок.0.7GBПри таких ограничениях это неизбежно приведет к невозможности напрямую оперировать большими объектами памяти в узле, такими как добавление2GBВсе файлы большого размера считываются в память для обработки строкового анализа, даже если физической памяти недостаточно.32GBОн не может полностью использовать ресурсы памяти компьютера, так почему же существует такой предел? Это должно восходить к началу разработки движка V8. Сначала он использовался только как среда выполнения JavaScript на стороне браузера. На самом деле мы редко сталкиваемся со сценариями, которые используют много памяти в браузере. стороны, поэтому нет необходимости устанавливать максимальный объем памяти. Но это только один аспект, на самом деле есть еще две основные причины:

  • JS单线程机制: Основная цель JS как языка сценариев браузера — взаимодействовать с пользователем и манипулировать DOM, поэтому это также определяет его природу как одного потока.Один поток означает, что исполняемый код должен выполняться по порядку, и может обрабатываться только одновременно с задачей. Представьте, что если JS многопоточен, один поток удаляет элемент DOM, а другой поток модифицирует элемент, то это неизбежно приведет к сложным проблемам синхронизации. Поскольку JS является однопоточным, это означает, что когда V8 выполняет сборку мусора, вся остальная логика в программе должна перейти в стадию ожидания pause, и логика JS не будет выполняться повторно, пока сборка мусора не закончится. Поэтому из-за однопоточного механизма JS процесс сборки мусора тормозит выполнение логики основного потока.

Хотя JS является однопоточным, чтобы в полной мере использовать вычислительную мощность многоядерного процессора операционной системы, в HTML5 был введен новый стандарт Web Worker, которому назначаются задачи для выполнения. Пока работает основной поток, Worker работает в фоновом режиме, и они не мешают друг другу. Подождите, пока рабочий поток завершит задачу вычисления, а затем верните результат в основной поток. Преимущество этого в том, что некоторые ресурсоемкие задачи или задачи с высокой задержкой ложатся на рабочий поток, а основной поток (обычно отвечающий за взаимодействие с пользовательским интерфейсом) будет работать очень плавно и не будет блокироваться или замедляться. Web Worker не является частью JS, а является функцией браузера, доступ к которой осуществляется через JS. Хотя он создает многопоточную среду выполнения, подпотоки полностью контролируются основным потоком и не могут получить доступ к специфичным для браузера API-интерфейсам, таким как управление DOM, поэтому этот новый стандарт не меняет однопоточной природы JS.

  • 垃圾回收机制: Сама по себе сборка мусора также является очень трудоемкой операцией, если предположить, что куча памяти V81.5G, то на небольшую сборку мусора у V8 уходит более 50 мс, а на неинкрементную сборку мусора уходит больше 1 с. Видно, что это занимает много времени. За это время 1 с браузер находится в состоянии ожидания, и в то же время он потеряет ответ пользователю.Если запущена анимация, это также приведет к зависанию анимации и пропуску кадров, что серьезно повлияет на производительность приложения. Поэтому, если использование памяти слишком велико, процесс сборки мусора неизбежно будет медленным, и чем дольше время ожидания основного потока, тем дольше браузер не получит ответа.

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

Попробуем ввести в командной строке узла следующую команду:

Версия узла, установленная автором локально,v10.14.2,доступныйnode -vПроверьте номер версии локального узла.В разных версиях следующие команды могут отличаться.

// 该命令可以用来查看node中可用的V8引擎的选项及其含义
node --v8-options

Затем мы увидим множество параметров V8 в окне командной строки, здесь мы сосредоточимся только на нескольких параметрах в красном поле на рисунке:

// 设置新生代内存中单个半空间的内存最小值,单位MB
node --min-semi-space-size=1024 xxx.js

// 设置新生代内存中单个半空间的内存最大值,单位MB
node --max-semi-space-size=1024 xxx.js

// 设置老生代内存最大值,单位MB
node --max-old-space-size=2048 xxx.js

С помощью вышеуказанных методов ограничение памяти, используемое движком V8, может быть ослаблено вручную, и узел также предоставляет намprocess.memoryUsage()Метод позволяет нам просмотреть фактический размер памяти, занимаемый текущим процессом node.

На приведенном выше рисунке значения нескольких включенных полей следующие, а единицы измерения — все байты:

  • heapTotal: Указывает общий размер динамической памяти, запрашиваемый в настоящее время V8.
  • heapUsed: указывает текущее использование памяти.
  • external: указывает объем памяти, занимаемый объектами C++ внутри V8.
  • rss(resident set size): указывает размер резидентного набора, то есть объем физической памяти, выделенный этому процессу узла, который содержит фрагменты кучи, стека и кода. Объекты, замыкания и т. д. хранятся в куче памяти, переменные хранятся в памяти стека, а фактический исходный код JavaScript хранится в памяти сегментов кода. При использовании рабочих потоковrssбудет значением, действительным для всего процесса, в то время как другие поля относятся только к текущему потоку.

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

3. Стратегия сборки мусора V8

Стратегия сборки мусора V8 в основном основана на分代式垃圾回收机制, который основан навремя жизни объектаСборка мусора памяти выполняется в разных поколениях, а затем для разных поколений используются разные алгоритмы сборки мусора.

3.1 Структура памяти V8

新生代老生代

  • 新生代(new_space)
  • 老生代(old_space)老生代指针区老生代数据区
  • 大对象区(large_object_space)
  • 代码区(code_space)
  • map区(map_space)

semispace(半空间)643232MB16MBScavenge

Scavenge

ScavengeCheneysemispaceFromToFromFromToFromToToFromFromTo

  • From

  • To

  • From

  • FromTo

  • From

  • To

  • From

  • FromTo

ScavengeFromTo

3.3 Продвижение объекта

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

  • Испытывал ли субъектScavengeалгоритм
  • ToПревышен ли коэффициент памяти пространства25%

По умолчанию создаваемые нами объекты размещаются вFromпространство, когда происходит сборка мусора, объекты удаляются изFromскопировать место вToПеред пробелом будет проверен адрес памяти объекта, чтобы определить, был ли он когда-то испытан.ScavengeАлгоритм, если адрес изменился, то объект будет передан в старую генерацию и не будет скопирован вToПространство можно представить следующей блок-схемой:

Если субъект не испытывалScavengeалгоритм, будет скопирован вToпространство, но если в это времяToСоотношение памяти пространства превысило25%, объект все равно будет переведен в старое поколение, как показано на следующем рисунке:

почему есть25%Ограничение памяти, потому чтоToкосмос испытал однаждыScavengeПосле того, как алгоритм суммируетFromПосле завершения смены ролей пространство станетFromпространство, последующие выделения памяти находятся вFromЕсли использование памяти слишком велико или даже переполняется, это повлияет на выделение последующих объектов, поэтому после превышения этого ограничения объекты будут напрямую переданы старому поколению для управления.

3.4 Старое поколение

В старом поколении из-за управления большим количеством уцелевших объектов, если вы все еще используетеScavengeЕсли алгоритм используется, он, очевидно, будет тратить половину памяти, поэтому он больше не используется.Scavengeалгоритм, но новый алгоритмMark-Sweep(标记清除)а такжеMark-Compact(标记整理)справляться.

Ранее мы, возможно, слышали об алгоритме под названием引用计数, принцип этого алгоритма относительно прост, он заключается в том, чтобы увидеть, есть ли у объекта другие ссылки, указывающие на него, если нет ссылки на объект, объект будет рассматриваться как мусор и будет утилизирован сборщиком мусора, пример следующий:

// 创建了两个对象obj1和obj2,其中obj2作为obj1的属性被obj1引用,因此不会被垃圾回收
let obj1 = {
    obj2: {
        a: 1
    }
}

// 创建obj3并将obj1赋值给obj3,让两个对象指向同一个内存地址
let obj3 = obj1;

// 将obj1重新赋值,此时原来obj1指向的对象现在只由obj3来表示
obj1 = null;

// 创建obj4并将obj3.obj2赋值给obj4
// 此时obj2所指向的对象有两个引用:一个是作为obj3的属性,另一个是变量obj4
let obj4 = obj3.obj2;

// 将obj3重新赋值,此时本可以对obj3指向的对象进行回收,但是因为obj3.obj2被obj4所引用,因此依旧不能被回收
obj3 = null;

// 此时obj3.obj2已经没有指向它的引用,因此obj3指向的对象在此时可以被回收
obj4 = null;

Вышеупомянутый пример в конечном счете будет удален сборщиком мусора после серии операций, но как только мы нажмем循环引用В сценарии будет проблема, рассмотрим следующий пример:

function foo() {
    let a = {};
    let b = {};
    a.a1 = b;
    b.b1 = a;
}
foo();

В этом примере мы будем возражатьaизa1свойство указывает на объектb, объектbизb1свойство указывает на объектa, образуя два объекта, ссылающихся друг на друга, вfooПосле выполнения функции область действия функции уничтожается, а переменные, содержащиеся в области видимости,aа такжеbЕго следовало переработать, но из-за引用计数алгоритма, обе переменные имеют ссылки на самих себя, поэтому они все еще не могут быть переработаны, что приводит к утечкам памяти.

Поэтому во избежание утечек памяти, вызванных циклическими ссылками, по состоянию на 2012 год все современные браузеры отказались от этого алгоритма и приняли новыйMark-Sweep(标记清除)а такжеMark-Compact(标记整理)алгоритм. В приведенном выше примере циклической ссылки, поскольку переменнаяaи переменнаяbневозможноwindowДоступ к глобальному объекту невозможен, поэтому он не может быть помечен, поэтому в конечном итоге он будет переработан.

Mark-Sweep(标记清除)разделен на标记а также清除Два этапа.На этапе маркировки просматриваются все объекты в куче, а затем помечаются живые объекты.На этапе очистки мертвые объекты очищаются.Mark-SweepАлгоритм в основном знает, должен ли объект быть переработан, оценивая, можно ли получить доступ к объекту.Конкретные шаги заключаются в следующем:

  • Сборщик мусора внутренне строит根列表Используется для поиска тех переменных, к которым можно получить доступ из корневого узла. Например, в JavaScriptwindowГлобальные объекты можно рассматривать как корневой узел.
  • Затем сборщик мусора стартует со всех корневых узлов, обходит дочерние узлы, до которых может добраться, и помечает их как активные, а места, до которых корневой узел добраться не может, неактивны и будут расцениваться как мусор.
  • Наконец, сборщик мусора освободит все неактивные блоки памяти и вернет их операционной системе.

В качестве корневого узла можно использовать следующие ситуации:

  1. глобальный объект
  2. Локальные переменные и параметры локальных функций
  3. Переменные и параметры других функций в текущей вложенной цепочке вызовов

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

Чтобы решить эту проблему фрагментации памяти,Mark-Compact(标记整理)Предложен алгоритм, который в основном используется для решения проблемы фрагментации памяти: после очистки мертвых объектов в процессе рециркуляции активные объекты будут перемещены в один конец кучи памяти в процессе сортировки, а движение завершено. Затем очистите всю память за границей, мы можем использовать следующую блок-схему для представления:

  • Предположим, что в старом поколении есть четыре объекта A, B, C и D.

  • в сборе мусора标记Фаза, отметьте объект A и объект C как активные

  • в сборе мусора整理Фаза, переместите активный объект в один конец кучи памяти

  • в сборе мусора清除Фаза, вся память в левой части активного объекта освобождается

На данный момент весь процесс сборки мусора старого поколения завершен.Как мы говорили в предыдущей статье, из-за однопоточного механизма JS процесс сборки мусора будет мешать выполнению основной задачи синхронизации потока, и возобновится после выполнения сборки мусора.Логика выполнения основной задачи, такое поведение называется全停顿(stop-the-world). В фазе маркировки выполнение основного потока также будет затруднено, вообще говоря, старое поколение сохранит большое количество уцелевших объектов, и если на фазе маркировки будет пройдена вся память кучи, то это неизбежно вызовет серьезные зависания.

Поэтому, чтобы сократить время паузы, вызванной сборкой мусора, двигатель V8 представилIncremental Marking(增量标记)Концепция обхода кучи памяти за один раз состоит в том, чтобы изменить операцию обхода кучи памяти за один раз на метод инкрементной маркировки, сначала помечая часть объектов в куче памяти, затем приостанавливая и повторно выполнение прямо в основном потоке JS после завершения задачи основного потока Продолжайте отмечать с того места, где была сделана исходная отметка паузы, пока не будет отмечена вся память кучи. Идея немного похожаReactв рамкеFiberАрхитектура, которая будет проходиться только во время простоя браузераFiber TreeВыполнять соответствующую задачу, в противном случае задерживать выполнение, как можно меньше влиять на задачи основного потока, избегать зависаний приложения и повышать производительность приложения.

Благодаря преимуществам поэтапной маркировки двигатель V8 продолжал внедряться延迟清理(lazy sweeping)а также增量式整理(incremental compaction), что делает процесс очистки и уборки поэтапным. В то же время, чтобы в полной мере использовать производительность многоядерного процессора, он также представит并行标记а также并行清理, чтобы еще больше уменьшить влияние сборки мусора на основной поток и повысить производительность приложения.

4. Как избежать утечек памяти

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

4.1 Создавайте как можно меньше глобальных переменных

В ES5 сvarКогда переменная создается в глобальной области видимости декларативным образом или когда переменная создается в области действия функции без какого-либо объявления, она невидимо монтируется вwindowна глобальном объекте, например:

var a = 1; // 等价于 window.a = 1;
function foo() {
    a = 1;
}

Эквивалентно

function foo() {
    window.a = 1;
}

мы вfooВ функции создается переменнаяaно забудь использоватьvarЧтобы объявить, в это время глобальная переменная будет неожиданно создана и смонтирована на объекте окна, и есть более тонкий способ создания глобальных переменных:

function foo() {
    this.a = 1;
}
foo(); // 相当于 window.foo()

когдаfooКогда функция вызывается, рабочий контекст, на который она указывает,windowглобальный объект, поэтому в функцииthisна самом деле указывает наwindow, также непреднамеренно создавая глобальную переменную. Когда происходит сборка мусора, на этапе маркировки, потому чтоwindowОбъект может использоваться как корневой узел, вwindowК свойствам, установленным выше, можно получить доступ и пометить их как активные и, таким образом, разместить их в памяти, поэтому они не будут собирать мусор, а глобальная область будет уничтожена только при завершении всего процесса. Если вы столкнулись с ситуацией, когда вы должны использовать глобальную переменную, обязательно установите глобальную переменную вnullЭто запускает механизм рециркуляции.

4.2 Ручной сброс таймера

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

const numbers = [];
const foo = function() {
    for(let i = 0;i < 100000;i++) {
        numbers.push(i);
    }
};
window.setInterval(foo, 1000);

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

4.3 Используйте замыкания экономно

Закрытие - это расширенная особенность JS, умное использование закрытия, чтобы помочь нам достичь многих расширенных функций. В целом, когда мы находим переменные, посмотрите в локальную область, не найдут выход изнутри вдоль цепочки области, но характеристики закрытий позволяет получать доступ к внутреннему действию за пределами объема переменных домена, например, :

function foo() {
    let local = 123;
    return function() {
        return local;
    }
}
const bar = foo();
console.log(bar()); // -> 123

В этом примереfooПосле того, как функция будет выполнена, она вернет анонимную функцию, которая внутренне ссылаетсяfooлокальные переменные в функцияхlocal, а через переменнуюbarЧтобы обратиться к этому определению анонимной функции, через это замыкание мы можемfooДоступ к своим локальным переменным из внешней области действия функцииlocal. Как правило, когдаfooПосле выполнения функции ее область видимости будет уничтожена, но из-за существования переменных, ссылающихся на анонимную функцию, которую она возвращает, область не может быть освобождена, что приводит кlocalПеременные не могут быть переработаны, и только когда мы отменим ссылку на анонимную функцию, она попадет на стадию сборки мусора.

4.4 Очистка ссылок DOM

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

const elements = {
    button: document.getElementById('button')
};

function removeButton() {
    document.body.removeChild(document.getElementById('button'));
}

В этом примере мы хотим вызватьremoveButtonспособ очиститьbuttonэлемент, но посколькуelementsпары существуют в словареbuttonссылка на элемент, поэтому даже если мы передадимremoveChildметод удаленbuttonэлемент, он по-прежнему хранится в памяти и не может быть освобожден, только мы вручную очищаемbuttonТолько ссылки на элементы удаляются сборщиком мусора.

4.5 слабые ссылки

Через первые несколько примеров мы обнаружим, что если мы будем небрежны, это легко вызовет проблему утечки памяти.С этой целью в ES6 были добавлены две эффективные структуры данных в ES6.WeakMapа такжеWeakSet, был рожден для решения проблемы утечек памяти. это выражает弱引用, все объекты, на которые ссылается его имя ключа, являются слабыми ссылками. Слабая ссылка означает, что ссылка на объект по имени ключа не будет учитываться в процессе сборки мусора. Пока объект, на который ссылается, не имеет других ссылок, механизм сборки мусора освободит память, занятую объектом. Это означает, что нам не нужно заботитьсяWeakMapСсылку имени среднего ключа на другие объекты очищать вручную не нужно, попробуем продемонстрировать процесс в узле (обратитесь к примеру Руана Ифэна во Введении в стандарт ES6 и реализуйте его вручную).

Сначала откройте командную строку узла и введите следующую команду:

node --expose-gc // --expose-gc 表示允许手动执行垃圾回收机制

Затем мы выполняем код ниже.

// 手动执行一次垃圾回收保证内存数据准确
> global.gc();
undefined

// 查看当前占用的内存,主要关心heapUsed字段,大小约为4.4MB
> process.memoryUsage();
{ rss: 21626880,
  heapTotal: 7585792,
  heapUsed: 4708440,
  external: 8710 }

// 创建一个WeakMap
> let wm = new WeakMap();
undefined

// 创建一个数组并赋值给变量key
> let key = new Array(1000000);
undefined

// 将WeakMap的键名指向该数组
// 此时该数组存在两个引用,一个是key,一个是WeakMap的键名
// 注意WeakMap是弱引用
> wm.set(key, 1);
WeakMap { [items unknown] }

// 手动执行一次垃圾回收
> global.gc();
undefined

// 再次查看内存占用大小,heapUsed已经增加到约12MB
> process.memoryUsage();
{ rss: 30232576,
  heapTotal: 17694720,
  heapUsed: 13068464,
  external: 8688 }

// 手动清除变量key对数组的引用
// 注意这里并没有清除WeakMap中键名对数组的引用
> key = null;
null

// 再次执行垃圾回收
> global.gc()
undefined

// 查看内存占用大小,发现heapUsed已经回到了之前的大小(这里约为4.8M,原来为4.4M,稍微有些浮动)
> process.memoryUsage();
{ rss: 22110208,
  heapTotal: 9158656,
  heapUsed: 5089752,
  external: 8698 }

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

5. Резюме

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

6. Общение

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

Статья обновлена ​​доБлог на гитхабе, Если вы считаете, что статья приемлема, добро пожаловать!

Один из ваших лайков заслуживает моих дополнительных усилий!

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