В недавнем проекте использовалось долговременное соединение.Вчера я обсуждал проблему с бэкендом: установление долговременного соединения с бэкендом в программе фронтенда.Если два долговременных соединения один и тот же аккаунт вызовет взаимные кики, то в браузере откроется две вкладки встречи.Не кикаем друг друга?
В это время я вдруг вспомнил точку знаний, которую видел раньше: в браузере вкладка на самом деле является процессом, а куча и стек каждого процесса независимы. Следовательно, при установлении постоянного соединения в куче выделяется только объект для вызова сетевых ресурсов операционной системы для связи, а два объекта, управляющих постоянным соединением в двух независимых процессах, можно рассматривать как независимые. Тогда, по идее, открытие двух вкладок в браузере ничем не отличается от входа по отдельности на двух компьютерах. Разумеется, помимо некоторых механизмов межпроцессного взаимодействия, которые могут быть предусмотрены внутри браузера.
Эта мысль вызвала у автора большой интерес к пониманию внутреннего хранилища процесса, поэтому в качестве примера для анализа возьмем серверный процесс узла. Как упоминалось в книге «Технология крупномасштабной разработки веб-сервисов», которую я читал ранее, дизайн фоновой бизнес-системы должен быть сосредоточен на двух узких местах, один из которых — загрузка ЦП, когда обратный прокси-сервер отправляет запрос на сервер приложений. также является нагрузкой, когда сервер приложений извлекает данные с сервера базы данных и генерирует запросы ввода-вывода. Целесообразно повысить эффективность доступа к вводу-выводу за счет увеличения кэш-памяти сервера базы данных. Но как мыслить, когда мы устраняем неполадки как независимый процесс?
Конечно, есть два параметра, которые вызовут сбой или смерть процесса: использование памяти и использование процессора.Мы в основном анализируем пространство для памяти, а основной анализ для процессора — это время. В этом руководстве мы в основном сосредоточимся на использовании памяти.
При анализе памяти мы в основном анализируем ее с помощью снимков памяти кучи.Движок V8 предоставляет внутренний интерфейс, который может напрямую экспортировать объекты JS в куче для анализа разработчиками. Мы используем модуль heapdump и выполняем следующую команду для установки:
npm install heapdump --save
Затем добавьте в код следующий оператор для выполнения этого модуля:
const heapdump = require('heapdump');
heapdump.writeSnapshot('./test' + '.heapsnapshot');
Для облегчения анализа мы написали демонстрацию службы узла экспресс,
var app = require('express')();
var http = require('http').Server(app);
var heapdump = require('heapdump');
var leakobjs = [];
function LeakClass(){
this.x = 1;
}
app.get('/', function(req, res){
console.log('get /');
for(var i = 0; i < 1000; i++){
leakobjs.push(new LeakClass());
}
res.send('<h1>Hello world</h1>');
});
setInterval(function(){
heapdump.writeSnapshot('./' + Date.now() + '.heapsnapshot');
}, 3000);
http.listen(3000, function(){
console.log('listening on port 3000');
});
В этой программе мы определяем массив leakobjs и LeakClass в глобальном домене.При поступлении каждого запроса объекты будут добавляться в массивleaks.Так как утечки находятся в глобальном домене, запрос не будет автоматически выпущен в конце, что приведет к утечке памяти.
Мы запрашивали эту услугу около 10 раз, и мы видим, что в каталоге проекта есть несколько файлов heapsnapshot. Мы используем текстовый редактор, чтобы проверить это. Мы видим, что содержимое внутри в основном представляет собой большой json, а основные поля снимки, узлы, ребра. где node представляет точку, а ребра представляют ребро, соединяющее точки. Поле node_fields в снимке в основном описывает значение шести чисел каждого узла в узлах Точно так же поле edge_fields также представляет значение каждого ребра в поле ребер. Строковое поле описывает имя точки и ребра, в том числе восстановление памяти в узле и узле «Корень GC».
Снимок кучи описывает взаимосвязь между точками и ребрами между ними. Итак, как куча памяти связана с точками и ребрами, описанными здесь?
Мы можем использовать глобальный домен в узле как корень и узел GC, из глобального домена, если мы можем получить доступ к 2 и 3 объектам из глобального домена, как 2, так и 3 могут получить доступ к 4 объектам. 3 может получить доступ к 5, 4 может получить доступ к 6. На левом рисунке, если узел 2 удален, к 4 все еще можно получить доступ из GC и узла, поэтому 2 не является доминирующим узлом 4. В соответствии с алгоритмом теории графов эталонный граф слева преобразуется в дерево доминирования, и можно получить отношение доминирования от GC к каждому узлу.
Итак, в чем польза изученного нами графа доминирования? Здесь представлены две концепции: мелкий размер и сохраненный размер. Неглубокий размер — это размер памяти, необходимой самому объекту при его создании, а Retained Size — размер памяти, который объект и его подчиненные узлы могут высвободить при удалении объекта из дерева доминирования.
Поскольку среда выполнения javascript в узле и браузере — v8, мы помещаем файл heapsnapshot, полученный ранее, в браузер для анализа: откройте режим отладки в chrome, выберите моментальный снимок кучи на вкладке памяти, нажмите «Загрузить», чтобы загрузить кучевой снимок файл, и вы можете увидеть дерево доминирования, проанализированное браузером.
После того, как браузер завершит обработку, переключимся в режим сводки, и мы увидим, что дерево доминаторов выглядит вот так. Правая часть настроена на Retained Size в порядке убывания, тогда мы будем более ясны в этот раз.Как правило, там, где происходят утечки памяти, память будет увеличиваться необоснованно, поэтому нам нужно только определить, какой узел верхнего уровня находится под Retained Size от большого к меньшему.Содержащиеся объекты занимают слишком много памяти, и тогда можно шаг за шагом определить объекты, у которых произошла утечка памяти, и найти место утечки памяти. При наблюдении (закрытии) объекты в трех красных прямоугольниках намного больше, чем другие узлы:
Давайте откроем объект app(), вы увидите, что размер объекта относительно исключения _router.
Итак, давайте продолжим соблюдать уровень на уровне, и, наконец, мы действительно имеем в виду, что сохраненный размер родительского узла слишком велик, потому что массив Leakobjs занимает слишком много памяти:
Если продолжить анализ router() и (), можно получить тот же результат.
Думая об этом, я вдруг почувствовал, что «внезапно оглядываюсь назад на книгу по операционной системе, которую я читал раньше, но человек был в тусклом свете». Может быть, теоретические знания, которые мы усвоили, просто мертвые знания, если мы не применяем их и не думаем о них в своей работе, и только после того, как мы их обдумаем и применим, мы сможем ожить.
Если вас заинтересовала эта статья и вы хотите обратить внимание на больше технических статей, обратите внимание на официальный аккаунт автора, и вам будет представлено больше хороших статей~