为什么这个 javascript 示例会泄漏内存?

问题描述 投票:0回答:1

我很困惑为什么这个代码示例存在内存泄漏。

// ephemeralLeak.js

async function promiseValue(value) {
  return value;
}

async function run() {
  for (;;) {
    await Promise.race([promiseValue("foo"), promiseValue("bar")]);
  }
}

run();

我在 Node 20.10.0 中运行它,就像...

node --max-old-space-size=512 test/examples/ephemeralLeak.js

结果是...

<--- Last few GCs --->

[13958:0x6859c60]     5576 ms: Scavenge (reduce) 509.6 (519.9) -> 508.8 (519.9) MB, 15.20 / 0.00 ms  (average mu = 0.285, current mu = 0.278) allocation failure; 
[13958:0x6859c60]     5630 ms: Scavenge (reduce) 509.8 (520.2) -> 509.0 (520.4) MB, 10.20 / 0.00 ms  (average mu = 0.285, current mu = 0.278) allocation failure; 
[13958:0x6859c60]     5684 ms: Scavenge (reduce) 510.1 (520.4) -> 509.4 (520.7) MB, 4.11 / 0.00 ms  (average mu = 0.285, current mu = 0.278) allocation failure; 


<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0xc9e850 node::Abort() [node]
 2: 0xb720ff  [node]
 3: 0xec1a70 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]
 4: 0xec1d57 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, v8::OOMDetails const&) [node]
 5: 0x10d3dc5  [node]
 6: 0x10d4354 v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [node]
 7: 0x10eb244 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::internal::GarbageCollectionReason, char const*) [node]
 8: 0x10eba5c v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
 9: 0x10edbba v8::internal::Heap::HandleGCRequest() [node]
10: 0x1058fd7 v8::internal::StackGuard::HandleInterrupts() [node]
11: 0x14fa98a v8::internal::Runtime_StackGuard(int, unsigned long*, v8::internal::Isolate*) [node]
12: 0x1934ef6  [node]
Aborted (core dumped)

我试图将其写为一个没有内存泄漏的示例,同时说明一些更有问题的代码。但是,它也有内存泄漏,我不明白为什么。

我知道

Promise.race()
对每个承诺都会调用
then
。因此,如果您继续遵守相同的承诺,就会有越来越多的
then
订阅者订阅您仍然可以参考的承诺。这无法被垃圾收集并导致内存泄漏。

请参阅这个已知的 Javascript 错误/功能,了解长期承诺所遇到的问题... https://github.com/nodejs/node/issues/17469

但是在上面的代码中,所有的 Promise 都是短暂的。它们仅持续一次循环迭代,并且应该能够被垃圾收集,因为没有保留引用。

尽管如此,我总是以

JavaScript heap out of memory

结束

有人能明白为什么这会导致内存泄漏吗?

任何人都可以编写相同的代码而不会泄漏吗?

javascript memory-leaks es6-promise
1个回答
0
投票

假设await的实现非常糟糕,以至于它永远不会“展开”已解决承诺的解析链,解决这个问题的唯一方法就是自己显式展开它。

在下面的示例中,这是通过调用(非异步)函数

run()
并提供显式回调来调用自身但没有异步/等待语法来实现的。这具有循环的效果,但没有内存泄漏和运行时失败。

async function promiseValue(value) {
  return value;
}

async function doRace() {
  await Promise.race([promiseValue("foo"), promiseValue("bar")]);
}

function run() {
  doRace().then(() => setImmediate(run));
}

run();

© www.soinside.com 2019 - 2024. All rights reserved.