常见 JavaScript 内存泄漏的原因

原文信息: 查看原文查看原文

Common Causes of Memory Leaks in JavaScript

- Trevor Indrek Lasn

识别并修复常见的 JavaScript 内存泄漏(Node.js 和 Deno.js)

内存泄漏是一种悄无声息的威胁,它逐渐降低性能,导致崩溃,并增加运营成本。与明显的错误不同,内存泄漏通常很微妙,很难发现,直到它们开始引起严重问题。

增加的内存使用推动了服务器成本的上升,并负面影响用户体验。理解内存泄漏是如何发生的是解决它们的第一步。

理解内存泄漏

当应用程序分配内存,然后在不再需要时未能释放它时,就会发生内存泄漏。随着时间的推移,这些未释放的内存块累积起来,导致内存消耗逐渐增加。

这在像 web 服务器这样的长时间运行的进程中尤为成问题,其中泄漏可能导致应用程序消耗越来越多的内存,直到最终崩溃或慢到几乎无法使用。

理解 Node.js(V8)中的内存使用

Node.js(V8)处理几种不同类型的内存。每种都在应用程序的性能和资源利用中起着关键作用。

内存类型描述
RSS(常驻集大小)Node.js 进程分配的总内存,包括内存的所有部分:代码、栈和堆。
堆总量为 JavaScript 对象分配的内存。这是分配堆的总大小。
堆已使用由 JavaScript 对象实际使用的内存。这显示了当前堆的使用量。
外部由链接到 JavaScript 对象的 C++ 对象使用的内存。此内存在 V8 堆外部管理。
数组缓冲区ArrayBuffer 对象分配的内存,用于保存原始二进制数据。
  1. RSS(常驻集大小): 为进程分配的总内存。

RSS 指的是 Node.js 进程的总内存占用。它包括为进程分配的所有内存,包括堆、栈和代码段。

// rss.js
console.log('初始内存使用情况:', process.memoryUsage());
setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`RSS: ${memoryUsage.rss}`);
}, 1000);

这个脚本每秒记录一次 RSS 内存使用情况。我们可以观察到总内存占用随时间的变化。

➜ node rss.js
初始内存使用情况: {
  rss: 38502400,
  heapTotal: 4702208,
  heapUsed: 2559000,
  external: 1089863,
  arrayBuffers: 10515
}
RSS: 41025536
RSS: 41041920
RSS: 41041920
RSS: 41041920
  1. 堆总量: 为 JavaScript 对象分配的内存量。

堆总量表示 V8 引擎(Node.js 使用的 JavaScript 引擎)为 JavaScript 对象分配的内存总量。

// heap.js
console.log('初始内存使用情况:', process.memoryUsage());
const largeArray = new Array(1e6).fill('A');
setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`堆总量: ${memoryUsage.heapTotal}`);
}, 1000);

分配一个大型数组会增加堆总量。记录的堆总量显示了为 JavaScript 对象分配的内存。

➜ node heap.js
初始内存使用情况: {
  rss: 38535168,
  heapTotal: 4702208,
  heapUsed: 2559224,
  external: 1089863,
  arrayBuffers: 10515
}
堆总量: 12976128
堆总量: 12976128
堆总量: 12976128
堆总量: 12976128
堆总量: 12976128
堆总量: 12976128
堆总量: 12976128
  1. 堆已使用: 对象实际使用的内存量。

堆已使用指的是当前由堆上的 JavaScript 对象使用的内存量。

当我们将对象推入数组时,我们正在增加堆使用的内存量。

// heap-used.js
console.log('初始内存使用情况:', process.memoryUsage());
let data = [];
for (let i = 0; i < 1e6; i++) {
    data.push({ index: i });
}
setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`堆已使用: ${memoryUsage.heapUsed}`);
}, 1000);

随着添加更多对象,堆已使用的值将会上升。

➜ node heap-used.js
初始内存使用情况: {
  rss: 38748160,
  heapTotal: 4702208,
  heapUsed: 2559424,
  external: 1089863,
  arrayBuffers: 10515
}
堆已使用: 2833808
堆已使用: 2847776
堆已使用: 2850800
堆已使用: 2854352
堆已使用: 2875800
堆已使用: 2879488
  1. 外部: 绑定到 JavaScript 的 C++ 对象使用的内存。

外部内存指的是通过绑定创建的、与 JavaScript 相关联的 C++ 对象所使用的内存。这些对象是通过绑定创建的,允许 JavaScript 与原生代码交互,在典型的 JavaScript 堆之外分配内存。

这部分内存在 JavaScript 中不直接可见,但仍然增加了应用程序所使用的总内存。

Buffer.alloc 方法分配了一个 50MB 的缓冲区,被追踪为外部内存。

// external.js
const buffer = Buffer.alloc(50 * 1024 * 1024); // 分配 50MB 的缓冲区
console.log('初始内存使用情况:', process.memoryUsage());
setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`外部内存: ${memoryUsage.external}`);
}, 1000);

这个例子记录了外部内存的使用情况,将反映缓冲区的分配。

➜ node external.js
初始内存使用情况: {
  rss: 39223296,
  heapTotal: 4702208,
  heapUsed: 2560832,
  external: 53518663,
  arrayBuffers: 52439315
}
外部内存: 53814435
外部内存: 53814435
外部内存: 53814435
外部内存: 53814435
外部内存: 53814435
外部内存: 53814435
外部内存: 53814435
  1. 数组缓冲区:ArrayBuffer 对象分配的内存。

数组缓冲区是用于 ArrayBuffer 对象的内存。这些对象在 JavaScript 中存储固定长度的二进制数据。

ArrayBuffer 是 JavaScript 类型数组系统的一部分,允许您直接使用二进制数据。

这些缓冲区的内存与常规 JavaScript 对象的内存分开跟踪。它们通常用于处理原始数据,如文件或网络协议。

以下是一个示例,我在这里分配了一个 50MB 的 ArrayBuffer,然后检查我的 Node.js 进程的初始内存使用情况。

// array-buffer.js
const buffer = new ArrayBuffer(50 * 1024 * 1024); // 50MB ArrayBuffer
console.log('初始内存使用情况:', process.memoryUsage());
setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`数组缓冲区: ${memoryUsage.arrayBuffers}`);
}, 1000);
➜ node array-buffer.js
初始内存使用情况: {
  rss: 39075840,
  heapTotal: 4702208,
  heapUsed: 2559496,
  external: 53518663,
  arrayBuffers: 52439315
}
数组缓冲区: 52439315
数组缓冲区: 52439315
数组缓冲区: 52439315
数组缓冲区: 52439315
数组缓冲区: 52439315
数组缓冲区: 52439315

常见 JavaScript 内存泄漏的原因

JavaScript 中的内存泄漏通常源于:

  1. 变量管理不当

管理不当的变量可能导致内存泄漏。

例如,如果您声明了应该临时的变量,但忘记清理它们,它们将继续占用内存。

let cache = {};
function storeData(key, value) {
    cache[key] = value;
}
// 模拟多次调用函数
storeData('item1', new Array(1000000).fill('A'));
storeData('item2', new Array(1000000).fill('B'));
// 内存泄漏:存储在 'cache' 中的数据从未被释放

在上面的例子中,数据被添加到一个名为 cache 的全局对象中。如果当这些数据不再需要时没有被移除,它将不必要地继续使用内存。

如果这些变量存储在全局作用域中,这尤其成问题,因为它们会在应用程序的整个生命周期中持续存在。

let globalUserSessions = {}; // 全局作用域
function addUserSession(sessionId, userData) {
  globalUserSessions[sessionId] = userData; // 在全局作用域中存储用户数据
}
function removeUserSession(sessionId) {
  delete globalUserSessions[sessionId]; // 手动移除用户会话
}
// 模拟添加用户会话
addUserSession('session1', { name: 'Alice', data: new Array(1000000).fill('A') });
addUserSession('session2', { name: 'Bob', data: new Array(1000000).fill('B') });
// 如果没有手动清理,globalUserSessions 对象将在整个应用程序生命周期中持续存在

globalUserSessions 是一个全局对象,用于存储用户会话数据。因为它在全局作用域中,所以它在整个应用程序运行时都存在。

如果会话没有使用 removeUserSession 正确移除,数据将无限期地保留在内存中,导致内存泄漏。

  1. 持久的全局对象

全局对象可以比需要的时间长地保留内存。它们中的数据在不再需要后仍可能保留在内存中。这会逐渐增加内存使用。

global.config = {
    settings: new Array(1000000).fill('Configuration')
};
// 内存泄漏:'config' 是全局的,将在整个应用程序生命周期中保留在内存中

由于 config 是全局可访问的并且从未被清除,它使用的内存将被保留在整个应用程序的运行期间。这里有一种我们可以避免内存泄漏的方法:

function createConfig() {
    return {
        settings: new Array(1000000).fill('Configuration')
    };
}
// 只在需要时使用 config,并让它之后被垃圾回收
function processConfig() {
    const config = createConfig();
    // 使用 config 执行操作
    console.log(config.settings[0]);
    // 一旦不再引用,config 将从内存中清除
}
processConfig();

我们不是将 config 存储在全局对象中,而是在函数内部局部存储 config。这确保了 config 在函数运行后被清除,为垃圾回收释放了内存。

  1. 未移除的事件监听器

如果添加了事件监听器并且在不再需要时没有正确移除,可能会导致内存泄漏。

每个事件监听器都保留了对函数及其使用的任何变量的引用,阻止垃圾回收器回收该内存。

随着时间的推移,如果你不断添加监听器而没有移除它们,这将导致内存使用增加。

以下示例演示了如果事件监听器没有被正确移除,如何导致内存泄漏:

const EventEmitter = require('events');
const myEmitter = new EventEmitter();
function listener() {
    console.log('事件触发了!');
}
// 重复添加事件监听器
setInterval(() => {
    myEmitter.on('event', listener);
}, 1000);

每秒添加一个新的事件监听器。然而,这些监听器从未被移除,导致它们在内存中累积。

每个监听器都持有对 listener 函数及其相关联的任何变量的引用,阻止垃圾回收,并导致内存使用随时间增加。

为了防止这种内存泄漏,当事件监听器不再需要时,你应该移除它们。

const EventEmitter = require('events');
const myEmitter = new EventEmitter();
function listener() {
    console.log('事件触发了!');
}
// 添加一个事件监听器
myEmitter.on('event', listener);
// 触发事件然后移除监听器
myEmitter.emit('event');
myEmitter.removeListener('event', listener);
// 或者,你可以使用 `once` 方法添加一个在触发后自动移除自身的监听器
myEmitter.once('event', listener);
  1. 捕获变量的闭包

JavaScript 中的闭包可能会无意中比需要的时间长地保留变量。当闭包捕获一个变量时,它会保留对该内存的引用。

如果闭包在长时间运行的过程中使用或没有正确终止,捕获的变量将保留在内存中,导致泄漏。

function createClosure() {
    let capturedVar = new Array(1000000).fill('Data');
    return function() {
        console.log(capturedVar[0]);
    };
}
const closure = createClosure();
// 即使不再使用,闭包也持有 'capturedVar'

为了避免泄漏,请确保闭包不会不必要地捕获大变量或在不再需要时结束它们。

function createClosure() {
    let capturedVar = new Array(1000000).fill('Data');
    return function() {
        console.log(capturedVar[0]);
        capturedVar = null; // 当不再需要时释放内存
    };
}
const closure = createClosure();
closure(); // 使用后释放 'capturedVar'
  1. 未管理的回调

在某些情况下,如果回调持有比必要时间更长的变量或对象的引用,可能会导致内存问题。

然而,JavaScript 的垃圾回收器通常在引用不再需要时有效地清理内存。

function fetchData(callback) {
    let data = new Array(1000000).fill('Data');
    setTimeout(() => {
        callback(data);
    }, 1000);
}
function handleData(data) {
    console.log(data[0]);
}
fetchData(handleData); // 'data' 数组仍然保留在内存中。

在上面的例子中:

  1. 数据分配: fetchData 函数分配了一个大数组(data),其中包含 100 万个元素。
  2. 回调引用: 回调函数(handleData)在 1 秒后被 setTimeout 调用时引用了这个大数组。

尽管分配了大量的内存,JavaScript 的垃圾回收器确保在不再需要时释放内存。

没有必要手动清除引用,除非你正在处理引用无意中被保留的非常复杂的情况。

避免不必要的复杂性

在大多数情况下,不需要在标准的异步回调中手动清除引用。

过于复杂(不推荐)

function fetchData(callback) {
  let data = new Array(1000000).fill('Data');
  setTimeout(() => {
      callback(data);
      data = null; // 释放引用
      global.gc(); // 显式触发垃圾回收
  }, 1000);
}
function handleData(data) {
  console.log(data[0]);
  data = null; // 处理后清除引用
}
console.log('初始内存使用情况:', process.memoryUsage());
fetchData(handleData);
setTimeout(() => {
  console.log('最终内存使用情况:', process.memoryUsage());
}, 2000); // 给垃圾回收一些时间

虽然此代码手动清除引用并显式触发垃圾回收,但它引入了不必要的复杂性。

JavaScript 的垃圾回收器通常足以处理内存清理,无需这些额外步骤。

在大多数情况下,这种手动干预不仅多余,还可能使代码更难维护。

  1. 错误使用 bind()

使用 bind() 可以创建一个新函数,其 this 关键字设置为特定值。如果你不小心,这可能会导致内存泄漏。

function MyClass() {
    this.largeData = new Array(1000000).fill('leak');
    window.addEventListener('click', this.handleClick.bind(this));
}
MyClass.prototype.handleClick = function() {
    console.log('点击了');
};
// 如果 MyClass 实例被销毁,但事件监听器没有被移除,
// 绑定的函数将保持实例在内存中的活动状态。

为什么 bind() 会导致内存泄漏

1. 引用被保留: 当你使用 bind() 时,新函数会记住原始函数和 this 值。如果你在不再需要时没有移除函数,它就会持续存在并占用内存。

2. 大对象保留在内存中: 绑定的函数可能会意外地使大对象保留在内存中,即使你不再需要它们。

  1. 循环引用

当两个对象相互引用时,就会发生循环引用。这会创建一个循环,可能会使垃圾回收器感到困惑,阻止它释放内存。

function CircularReference() {
    this.reference = this; // 循环引用
}
let obj = new CircularReference();
obj = null; // 将 obj 设置为 null 可能不会释放内存。

即使你将 obj 设置为 null,由于自循环,内存可能不会被释放。

如何避免循环引用

  1. 打破循环: 确保对象在不再需要时不要相互引用。这有助于垃圾回收器清除它们。
function CircularReference() {
    this.reference = this;
}
let obj = new CircularReference();
// 打破循环引用
obj.reference = null; 
obj = null; // 现在内存可以被释放了

通过将 obj.reference 设置为 null,我们打破了循环引用。这允许垃圾回收器在 obj 不再被需要时释放内存。

  1. 使用弱引用: 使用 WeakMapWeakSetWeakRef 允许垃圾回收器即使存在引用也能清理内存,只要它们是弱的。
let weakMap = new WeakMap();
function CircularReference() {
    let obj = {};
    weakMap.set(obj, "这是一个弱引用");
    return obj;
}
let obj = CircularReference();
// 当不再需要时,对象可以被垃圾回收

weakMap 持有对 obj 的弱引用。这意味着当 obj 在其他地方不再被使用时,即使它在 weakMap 中被引用,它仍然可以被垃圾回收。

let weakRef;
function createObject() {
    let obj = { data: '重要' };
    weakRef = new WeakRef(obj);
    return obj;
}
let obj = createObject();
console.log(weakRef.deref()); // { data: '重要' }
obj = null; // 现在对象可以被垃圾回收了

weakRef 允许你对 obj 持有一个弱引用。如果 obj 被设置为 null 并且没有其他引用它,即使 weakRef 仍然存在,它也可以被垃圾回收。

快速说明

WeakMapWeakSetWeakRef 有助于防止内存泄漏,但你可能并不总是需要它们。它们更多用于高级用例,如管理缓存或大型数据。

如果你正在处理典型的 web 应用程序,你可能不会经常看到它们,但知道它们存在并在需要时可以使用是很好的。

在 Node.js 中分析内存使用情况

要找到内存泄漏,你需要分析你的应用程序以了解内存是如何被使用的。

以下是一个 Node.js 应用程序,旨在模拟 CPU 密集型任务、I/O 操作,并有意为测试目的创建内存泄漏。

const http = require('http');
const url = require('url');
// 模拟 CPU 密集型任务
const handleCpuIntensiveTask = (req, res) => {
    let result = 0;
    for (let i = 0; i < 1e7; i++) {
        result += i * Math.random();
    }
    console.log('内存使用情况(CPU 任务):', process.memoryUsage()); // 日志内存使用情况
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end(`CPU 密集型任务的结果: ${result}`);
};
// 创建一个大的内存缓冲区
const largeBuffer = Buffer.alloc(1024 * 1024 * 50, 'a'); // 用 'a' 填充的 50MB 缓冲区
// 模拟 I/O 操作
const handleSimulateIo = (req, res) => {
    // 模拟读取缓冲区,就好像它是一个文件
    setTimeout(() => {
        console.log('内存使用情况(模拟 I/O):', process.memoryUsage()); // 日志内存使用情况
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end(`模拟 I/O 操作完成,数据长度: ${largeBuffer.length}`);
    }, 500); // 模拟 500ms I/O 操作
};
// 模拟内存泄漏(用于测试)
let memoryLeakArray = [];
const causeMemoryLeak = () => {
    memoryLeakArray.push(new Array(1000).fill('内存泄漏'));
    console.log('内存泄漏数组长度:', memoryLeakArray.length);
};
const server = http.createServer((req, res) => {
    const parsedUrl = url.parse(req.url, true);
    if (parsedUrl.pathname === '/cpu-intensive') {
        handleCpuIntensiveTask(req, res);
    } else if (parsedUrl.pathname === '/simulate-io') {
        handleSimulateIo(req, res);
    } else if (parsedUrl.pathname === '/cause-memory-leak') {
        causeMemoryLeak();
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('已造成内存泄漏。检查内存使用情况。');
    } else {
        res.writeHead(404, { 'Content-Type': 'text/plain' });
        res.end('未找到');
    }
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
    console.log(`服务器正在端口 ${PORT} 上运行`);
});

接下来,我们需要对服务器进行压力测试。这个脚本通过发送 100 个请求来模拟 CPU、I/O 和内存泄漏。

#!/bin/bash
# 发送的请求数量
REQUESTS=100
# 端点 URL
CPU_INTENSIVE_URL="http://localhost:3000/cpu-intensive"
SIMULATE_IO_URL="http://localhost:3000/simulate-io"
MEMORY_LEAK_URL="http://localhost:3000/cause-memory-leak"
echo "向 $CPU_INTENSIVE_URL 和 $SIMULATE_IO_URL 发送 $REQUESTS 个请求..."
# 循环 CPU 密集型端点
for ((i=1;i<=REQUESTS;i++)); do
  curl -s $CPU_INTENSIVE_URL > /dev/null &
done
# 循环模拟 I/O 端点
for ((i=1;i<=REQUESTS;i++)); do
  curl -s $SIMULATE_IO_URL > /dev/null &
done
# 循环内存泄漏端点
for ((i=1;i<=REQUESTS;i++)); do
  curl -s $MEMORY_LEAK_URL > /dev/null &
done
wait
echo "完成。"

它循环遍历 URL 并使用 curl 发送静默请求,将它们在后台运行以模拟高负载。

➜  ./load_test.sh
向 http://localhost:3000/cpu-intensive 和 http://localhost:3000/simulate-io 和 http://localhost:3000/cause-memory-leak 发送 100 个请求
完成。

以下是我们的服务器对压力测试的响应。在开始测试之前,请确保服务器正在运行。

➜  node --prof server.js
服务器正在端口 3000 上运行
内存使用情况(模拟 I/O): {
  rss: 122863616,
  heapTotal: 17547264,
  heapUsed: 8668016,
  external: 54075004,
  arrayBuffers: 52439275
}
内存泄漏数组长度: 25
内存泄漏数组长度: 26
内存泄漏数组长度: 27
内存泄漏数组长度: 28
内存泄漏数组长度: 29
内存泄漏数组长度: 30
内存泄漏数组长度: 31
内存泄漏数组长度: 32
内存泄漏数组长度: 33
内存泄漏数组长度: 34
内存泄漏数组长度: 35
内存泄漏数组长度: 36
内存泄漏数组长度: 37
内存泄漏数组长度: 38
内存泄漏数组长度: 39
内存泄漏数组长度: 40
内存泄漏数组长度: 41
内存泄漏数组长度: 42
内存泄漏数组长度: 43
内存泄漏数组长度: 44
内存泄漏数组长度: 45
内存泄漏数组长度: 46
内存泄漏数组长度: 47
内存泄漏数组长度: 48
内存泄漏数组长度: 49
内存泄漏数组长度: 50
内存泄漏数组长度: 51
内存泄漏数组长度: 52
内存泄漏数组长度: 53
内存泄漏数组长度: 54
内存使用情况(CPU 任务): {
  rss: 122716160,
  heapTotal: 17547264,
  heapUsed: 11393456,
  external: 54075004,
  arrayBuffers: 52439275
}
内存泄漏数组长度: 173

分析结果

分析数据将被保存在一个名为 isolate-0xXXXXXXXXXXXX-v8.log 的文件中。

要处理日志并获得人类可读的摘要,请运行:

➜  node --prof-process isolate-0x140008000-42065-v8.log > processed-profile.txt

这将生成一个包含 CPU 分析数据的 processed-profile.txt 文件,其中包括应用程序花费时间的详细信息以及它如何管理内存。

打开 processed-profile.txt 文件,查找应用程序使用大量时间或内存的区域。

Statistical profiling result from isolate-0x140008000-42065-v8.log, (4099 ticks, 308 unaccounted, 0 excluded).

 [Shared libraries]:
   ticks  total  nonlib   name

 [JavaScript]:
   ticks  total  nonlib   name
   1007   24.6%   24.6%  JS: *handleCpuIntensiveTask /Users/trevorindreklasn/Projects/labs/node-memory/server.js:5:32
      5    0.1%    0.1%  JS: +handleCpuIntensiveTask /Users/trevorindreklasn/Projects/labs/node-memory/server.js:5:32
      1    0.0%    0.0%  JS: ^onParserExecute node:_http_server:839:25
      1    0.0%    0.0%  JS: ^getKeys node:internal/util/inspect:709:17
      1    0.0%    0.0%  JS: ^clearBuffer node:internal/streams/writable:742:21
      1    0.0%    0.0%  JS: ^checkListener node:events:276:23
      1    0.0%    0.0%  JS: ^Socket node:net:353:16
      1    0.0%    0.0%  JS: +pushAsyncContext node:internal/async_hooks:539:26
      1    0.0%    0.0%  JS: +processTicksAndRejections node:internal/process/task_queues:67:35

 [C++]:
   ticks  total  nonlib   name
   2772   67.6%   67.6%  t std::__1::__hash_table<std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>, std::__1::__unordered_map_hasher<int, std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>, std::__1::hash<int>, std::__1::equal_to<int>, true>, std::__1::__unordered_map_equal<int, std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>, std::__1::equal_to<int>, std::__1::hash<int>, true>, std::__1::allocator<std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>>>::rehash(unsigned long)

 [Summary]:
   ticks  total  nonlib   name
   1019   24.9%   24.9%  JavaScript
   2772   67.6%   67.6%  C++
    358    8.7%    8.7%  GC
      0    0.0%          Shared libraries
    308    7.5%          Unaccounted

 [C++ entry points]:
   ticks    cpp   total   name
   2636  100.0%   64.3%  TOTAL

 [Bottom up (heavy) profile]:
  Note: percentage shows a share of a particular caller in the total
  amount of its parent calls.
  Callers occupying less than 1.0% are not shown.

   ticks parent  name
   2772   67.6%  t std::__1::__hash_table<std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>, std::__1::__unordered_map_hasher<int, std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>, std::__1::hash<int>, std::__1::equal_to<int>, true>, std::__1::__unordered_map_equal<int, std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>, std::__1::equal_to<int>, std::__1::hash<int>, true>, std::__1::allocator<std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>>>::rehash(unsigned long)
   1880   67.8%    JS: *handleCpuIntensiveTask /Users/trevorindreklasn/Projects/labs/node-memory/server.js:5:32
   1727   91.9%      JS: ^<anonymous> /Users/trevorindreklasn/Projects/labs/node-memory/server.js:36:34
   1129   65.4%        JS: +emit node:events:467:44
   1129  100.0%          JS: ^parserOnIncoming node:_http_server:1033:26
   1129  100.0%            JS: ^parserOnHeadersComplete node:_http_common:71:33
    598   34.6%        JS: ^emit node:events:467:44
    598  100.0%          JS: ^parserOnIncoming node:_http_server:1033:26
    598  100.0%            JS: ^parserOnHeadersComplete node:_http_common:71:33
    153    8.1%      JS: ~<anonymous> /Users/trevorindreklasn/Projects/labs/node-memory/server.js:36:34
    140   91.5%        JS: ^emit node:events:467:44
    140  100.0%          JS: ~parserOnIncoming node:_http_server:1033:26
    140  100.0%            JS: ~parserOnHeadersComplete node:_http_common:71:33
     13    8.5%        JS: ~parserOnIncoming node:_http_server:1033:26
     13  100.0%          JS: ~parserOnHeadersComplete node:_http_common:71:33
    655   23.6%    t std::__1::__hash_table<std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>, std::__1::__unordered_map_hasher<int, std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>, std::__1::hash<int>, std::__1::equal_to<int>, true>, std::__1::__unordered_map_equal<int, std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>, std::__1::equal_to<int>, std::__1::hash<int>, true>, std::__1::allocator<std::__1::__hash_value_type<int, std::__1::unique_ptr<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>, std::__1::default_delete<std::__1::unordered_map<int, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<int const, std::__1::unique_ptr<v8_inspector::InspectedContext, std::__1::default_delete<v8_inspector::InspectedContext>>>>>>>>>>::rehash(unsigned long)
    654   99.8%      JS: *handleCpuIntensiveTask /Users/trevorindreklasn/Projects/labs/node-memory/server.js:5:32
    612   93.6%        JS: ^<anonymous> /Users/trevorindreklasn/Projects/labs/node-memory/server.js:36:34
    410   67.0%          JS: +emit node:events:467:44
    410  100.0%            JS: ^parserOnIncoming node:_http_server:1033:26
    202   33.0%          JS: ^emit node:events:467:44
    202  100.0%            JS: ^parserOnIncoming node:_http_server:1033:26
     42    6.4%        JS: ~<anonymous> /Users/trevorindreklasn/Projects/labs/node-memory/server.js:36:34
     40   95.2%          JS: ^emit node:events:467:44
     40  100.0%            JS: ~parserOnIncoming node:_http_server:1033:26
      2    4.8%          JS: ~parserOnIncoming node:_http_server:1033:26
      2  100.0%            JS: ~parserOnHeadersComplete node:_http_common:71:33
     49    1.8%    JS: ^<anonymous> /Users/trevorindreklasn/Projects/labs/node-memory/server.js:36:34
     38   77.6%      JS: +emit node:events:467:44
     38  100.0%        JS: ^parserOnIncoming node:_http_server:1033:26
     38  100.0%          JS: ^parserOnHeadersComplete node:_http_common:71:33
     11   22.4%      JS: ^emit node:events:467:44
     11  100.0%        JS: ^parserOnIncoming node:_http_server:1033:26
     11  100.0%          JS: ^parserOnHeadersComplete node:_http_common:71:33

   1007   24.6%  JS: *handleCpuIntensiveTask /Users/trevorindreklasn/Projects/labs/node-memory/server.js:5:32
    940   93.3%    JS: ^<anonymous> /Users/trevorindreklasn/Projects/labs/node-memory/server.js:36:34
    663   70.5%      JS: +emit node:events:467:44
    663  100.0%        JS: ^parserOnIncoming node:_http_server:1033:26
    663  100.0%          JS: ^parserOnHeadersComplete node:_http_common:71:33
    277   29.5%      JS: ^emit node:events:467:44
    277  100.0%        JS: ^parserOnIncoming node:_http_server:1033:26
    277  100.0%          JS: ^parserOnHeadersComplete node:_http_common:71:33
     67    6.7%    JS: ~<anonymous> /Users/trevorindreklasn/Projects/labs/node-memory/server.js:36:34
     61   91.0%      JS: ^emit node:events:467:44
     61  100.0%        JS: ~parserOnIncoming node:_http_server:1033:26
     61  100.0%          JS: ~parserOnHeadersComplete node:_http_common:71:33
      6    9.0%      JS: ~parserOnIncoming node:_http_server:1033:26
      6  100.0%        JS: ~parserOnHeadersComplete node:_http_common:71:33

    308    7.5%  UNKNOWN
     11    3.6%    JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
     11  100.0%      JS: ^requireBuiltin node:internal/bootstrap/realm:421:24
      2   18.2%        JS: ~<anonymous> node:internal/streams/duplex:1:1
      2  100.0%          JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
      2  100.0%            JS: ^requireBuiltin node:internal/bootstrap/realm:421:24
      2   18.2%        JS: ~<anonymous> node:http:1:1
      2  100.0%          JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
      2  100.0%            JS: ~compileForPublicLoader node:internal/bootstrap/realm:332:25
      1    9.1%        JS: ~<anonymous> node:net:1:1
      1  100.0%          JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
      1  100.0%            JS: ^requireBuiltin node:internal/bootstrap/realm:421:24
      1    9.1%        JS: ~<anonymous> node:internal/streams/readable:1:1
      1  100.0%          JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
      1  100.0%            JS: ^requireBuiltin node:internal/bootstrap/realm:421:24
      1    9.1%        JS: ~<anonymous> node:internal/streams/operators:1:1
      1  100.0%          JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
      1  100.0%            JS: ^requireBuiltin node:internal/bootstrap/realm:421:24
      1    9.1%        JS: ~<anonymous> node:internal/perf/observe:1:1
      1  100.0%          JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
      1  100.0%            JS: ^requireBuiltin node:internal/bootstrap/realm:421:24
      1    9.1%        JS: ~<anonymous> node:internal/child_process:1:1
      1  100.0%          JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
      1  100.0%            JS: ^requireBuiltin node:internal/bootstrap/realm:421:24
      1    9.1%        JS: ~<anonymous> node:child_process:1:1
      1  100.0%          JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
      1  100.0%            JS: ^requireBuiltin node:internal/bootstrap/realm:421:24
      1    9.1%        JS: ~<anonymous> node:_http_agent:1:1
      1  100.0%          JS: ^compileForInternalLoader node:internal/bootstrap/realm:384:27
      1  100.0%            JS: ^requireBuiltin node:internal/bootstrap/realm:421:24

特别关注以下方面: 高 CPU 使用率的函数:这些是你的代码中的瓶颈。 内存密集型函数:消耗大量内存的函数可能指向潜在的内存泄漏,特别是当它们对应于应该释放内存但实际没有的代码部分时。 事件循环和垃圾回收(GC):注意在 GC 中花费的高百分比时间,因为这可能表明应用程序在内存管理上遇到了困难。 内存泄漏可能很微妙,但解决它们是保持你的 JavaScript 应用程序高效和可靠的关键。

分享于 2024-08-22

访问量 130

预览图片