JavaScript 中内存泄漏的常见原因

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

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

内存使用增加会推高服务器成本,并对用户体验产生负面影响。了解内存泄漏的发生方式是解决这些问题的第一步。

理解内存泄漏

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

在像 Web 服务器这样的长期运行进程中,这种情况尤其严重,因为泄漏会导致应用程序消耗越来越多的内存,直到最终崩溃或严重变慢。

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

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

内存类型 描述
RSS (Resident Set Size) 为 Node.js 进程分配的总内存,包括内存的所有部分:代码、栈和堆。
Heap Total 为 JavaScript 对象分配的内存。这是分配堆的总大小。
Heap Used 实际被 JavaScript 对象使用的内存。这表示当前堆使用了多少内存。
External 由链接到 JavaScript 对象的 C++ 对象使用的内存。这部分内存是在 V8 堆之外管理的。
Array Buffers 为 ArrayBuffer 对象分配的内存,这些对象用于存储原始二进制数据。

RSS (Resident Set Size):为进程分配的总内存

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

// rss.js
console.log('Initial Memory Usage:', process.memoryUsage());

setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`RSS: ${memoryUsage.rss}`);
}, 1000);

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

➜ node rss.js
Initial Memory Usage: {
  rss: 38502400,
  heapTotal: 4702208,
  heapUsed: 2559000,
  external: 1089863,
  arrayBuffers: 10515
}
RSS: 41025536
RSS: 41041920
RSS: 41041920
RSS: 41041920

Heap Total:为 JavaScript 对象分配的内存量

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

// heap.js
console.log('Initial Memory Usage:', process.memoryUsage());

const largeArray = new Array(1e6).fill('A');

setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`Heap Total: ${memoryUsage.heapTotal}`);
}, 1000);

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

➜ node heap.js
Initial Memory Usage: {
  rss: 38535168,
  heapTotal: 4702208,
  heapUsed: 2559224,
  external: 1089863,
  arrayBuffers: 10515
}
Heap Total: 12976128
Heap Total: 12976128
Heap Total: 12976128

Heap Used:实际被对象使用的内存量

Heap Used 指的是当前 JavaScript对象在堆上使用的内存量。

当我们将对象添加到数组中时,堆使用的内存量会增加。

// heap-used.js
console.log('Initial Memory Usage:', process.memoryUsage());

let data = [];
for (let i = 0; i < 1e6; i++) {
    data.push({ index: i });
}

setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`Heap Used: ${memoryUsage.heapUsed}`);
}, 1000);

随着更多对象的添加,堆使用的值会增加。

➜ node heap-used.js
Initial Memory Usage: {
  rss: 38748160,
  heapTotal: 4702208,
  heapUsed: 2559424,
  external: 1089863,
  arrayBuffers: 10515
}
Heap Used: 2833808
Heap Used: 2847776
Heap Used: 2850800

External:由绑定到 JavaScript 的 C++ 对象使用的内存

External 内存指的是由链接到JavaScriptC++对象使用的内存。这些对象通过绑定创建,使 JavaScript 能够与本地代码交互,从而在典型的 JavaScript堆之外分配内存。

这些内存在 JavaScript中并不直接可见,但它们仍然增加了应用程序的总内存使用量。

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

// external.js
const buffer = Buffer.alloc(50 * 1024 * 1024); // 分配 50MB 缓冲区

console.log('Initial Memory Usage:', process.memoryUsage());

setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`External Memory: ${memoryUsage.external}`);
}, 1000);

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

➜ node external.js
Initial Memory Usage: {
  rss: 39223296,
  heapTotal: 4702208,
  heapUsed: 2560832,
  external: 53518663,
  arrayBuffers: 52439315
}
External Memory: 53814435
External Memory: 53814435

Array Buffers:为 ArrayBuffer 对象分配的内存

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

ArrayBufferJavaScript类型数组系统的一部分,允许您直接处理二进制数据。

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

以下是分配 50MB ArrayBuffer并检查 Node.js进程初始内存使用情况的示例。

// array-buffer.js
const buffer = new ArrayBuffer(50 * 1024 * 1024); // 50MB ArrayBuffer

console.log('Initial Memory Usage:', process.memoryUsage());

setInterval(() => {
    const memoryUsage = process.memoryUsage();
    console.log(`Array Buffers: ${memoryUsage.arrayBuffers}`);
}, 1000);
➜ node array-buffer.js
Initial Memory Usage: {
  rss: 39075840,
  heapTotal: 4702208,
  heapUsed: 2559496,
  external: 53518663,
  arrayBuffers: 52439315
}
Array Buffers: 52439315
Array Buffers: 52439315

JavaScript 中内存泄漏的常见原因

JavaScript 中的内存泄漏通常由以下原因引起:

变量管理不当

未正确管理的变量可能导致内存泄漏。

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

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 是一个全局对象,用于存储用户会话数据。由于它位于全局作用域中,因此在应用程序的整个运行期间都会存在。如果未使用 removeUserSession 正确移除会话数据,数据将无限期地留在内存中,导致内存泄漏。

持久的全局对象

全局对象可能比需要的时间更长地占用内存。它们中的数据即使不再需要也可能继续保留在内存中,从而逐渐增加内存使用量。

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 在函数运行后被清除,释放内存供垃圾回收使用。

未移除的事件监听器

在不再需要时,未正确移除事件监听器可能导致内存泄漏。

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

随着时间的推移,如果不断添加监听器而不移除它们,内存使用量将会增加。

以下是一个展示事件监听器未正确移除时如何导致内存泄漏的例子:

const EventEmitter = require('events');
const myEmitter = new EventEmitter();

function listener() {
    console.log('Event triggered!');
}

// 不断添加事件监听器
setInterval(() => {
    myEmitter.on('event', listener);
}, 1000);

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

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

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

const EventEmitter = require('events');
const myEmitter = new EventEmitter();

function listener() {
    console.log('Event triggered!');
}

// 添加事件监听器
myEmitter.on('event', listener);

// 触发事件后移除监听器
myEmitter.emit('event');
myEmitter.removeListener('event', listener);

// 或者,您可以使用 `once` 方法添加一个在触发后自动移除的监听器
myEmitter.once('event', listener);

闭包捕获变量

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' 被释放

未管理的回调

在某些情况下,如果回调持有变量或对象的时间超过必要时长,未管理的回调可能会导致内存问题。

然而,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' 数组保留在内存中

在上面的例子中:

  • 数据分配fetchData 函数分配了一个包含 100 万个元素的大数组(data)。
  • 回调引用:当 setTimeout 在 1 秒后调用回调函数(handleData)时,它引用了这个大数组。

尽管进行了大规模的分配,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 的垃圾回收机制通常足以处理内存清理,不需要这些额外的步骤。在大多数情况下,这种手动干预不仅是多余的,还可能使代码更难维护。

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('Clicked');
};

// 如果 MyClass 的实例被销毁,但事件监听器未移除,
// 绑定的函数会导致该实例在内存中保持活跃状态。

内存泄漏发生在 bind() 的原因

  1. 引用被保留:使用 bind() 时,新函数会记住原始函数和 this 值。如果在不再需要时不移除函数,它会继续占用内存。
  2. 大对象保持在内存中:绑定的函数可能意外地使大对象保留在内存中,即使你已经不再需要它们。

循环引用

当两个对象互相引用时,就会发生循环引用。这种情况会造成一个循环,可能会让垃圾回收器无法释放内存。

function CircularReference() {
    this.reference = this; // 循环引用
}

let obj = new CircularReference();
obj = null; // 将 obj 设为 null 可能无法释放内存。

即使你将 obj 设为 null,由于存在自引用,内存可能仍然无法释放。

如何避免循环引用
打破循环:确保对象在不再需要时不会相互引用,这有助于垃圾回收器清理它们。

function CircularReference() {
    this.reference = this;
}

let obj = new CircularReference();

// 打破循环引用
obj.reference = null; 
obj = null; // 现在可以释放内存了

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

使用弱引用:使用 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: 'important' };
    weakRef = new WeakRef(obj);
    return obj;
}

let obj = createObject();

console.log(weakRef.deref()); // { data: 'important' }

obj = null; // 现在对象可以被垃圾回收

weakRef 允许你对 obj 保持弱引用。如果将 obj 设为 null 且没有其他地方引用它,那么即使 weakRef 仍然存在,它也可以被垃圾回收。

快速提示

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

如果你在开发典型的 Web 应用,可能不常见到它们,但了解它们的存在并在需要时使用是有帮助的。

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

为了找到内存泄漏,你需要分析应用程序,以了解内存的使用情况。

以下是一个用于模拟 CPU密集型任务、I/O操作,并故意制造内存泄漏以供测试的Node.js 应用程序。

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('memory leak'));
    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 个请求分别模拟 CPUI/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 "发送 $REQUESTS 个请求到 $CPU_INTENSIVE_URL 和 $SIMULATE_IO_URL..."

# 循环 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 "完成。"

此脚本使用 curl 循环遍历 URL,并在后台运行它们以模拟高负载。

➜  ./load_test.sh
发送 100 个请求到 http://localhost:3000/cpu-intensive 和 http://localhost:3000/simulate-io 和

 http://localhost:3000/cause-memory-leak
完成。

以下是服务器对压力测试的响应。确保在开始测试前服务器已经运行。

➜  node --prof server.js
服务器正在端口 3000 上运行
内存使用情况 (模拟 I/O): {
  rss: 122863616,
  heapTotal: 17547264,
  heapUsed: 8668016,
  external: 54075004,
  arrayBuffers: 52439275
}
内存泄漏数组长度: 25
内存泄漏数组长度: 26
内存泄漏数组长度: 27
内存泄漏数组长度: 28
内存泄漏数组长度: 29
...
内存泄漏数组长度: 56
内存使用情况 (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

这将生成一个名为 processed-profile.txt 的文件,其中包含 CPU 配置文件的数据,详细说明了您的应用程序花费时间的位置及其如何管理内存。

打开 processed-profile.txt 文件,查找占用大量时间或内存的部分。

isolate-0x140008000-42065-v8.log 中统计的配置文件结果(4099 个 ticks,308 个未计入,0 个排除)。

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应用程序的高效和可靠至关重要。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容