const axios = require('axios');
const JSDOM = require('jsdom').JSDOM;
axios.get('https://facebook.com')
.then(response => {
let i=1;
while (true) {
console.log(i++);
const dom = new JSDOM(response.data);
dom.window.close();
}
});
以上代码将在我的计算机上运行440次,然后崩溃,并显示以下内容:
<--- Last few GCs --->
[13411:0x5dc70d0] 63566 ms: Mark-sweep 2042.3 (2051.1) -> 2041.3 (2051.1) MB, 2407.4 / 0.0 ms (average mu = 0.123, current mu = 0.027) allocation failure scavenge might not succeed
[13411:0x5dc70d0] 67327 ms: Mark-sweep 2042.0 (2051.1) -> 2041.1 (2050.9) MB, 3745.2 / 0.0 ms (average mu = 0.055, current mu = 0.004) allocation failure scavenge might not succeed
<--- JS stacktrace --->
==== JS stack trace =========================================
0: ExitFrame [pc: 0x145cc79]
1: StubFrame [pc: 0x145dab5]
Security context: 0x0d2e98240921 <JSObject>
2: /* anonymous */ [0x374dcc798739] [/pathtomyproject/node_modules/parse5/lib/tokenizer/index.js:~644] [pc=0x323d7cd98d24](this=0x10b528b73279 <Tokenizer map = 0x242f38ec5c11>,95)
3: getNextToken [0x374dcc799419] [/pathtomyproject/node_modules/parse5/lib/tokenizer/i...
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Writing Node.js report to file: report.20200215.163602.13411.0.001.json
Node.js report completed
1: 0xa9d570 node::Abort() [node]
2: 0xa9f832 node::OnFatalError(char const*, char const*) [node]
3: 0xc0758e v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
4: 0xc07909 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
5: 0xdb5e15 [node]
6: 0xdb64a6 v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [node]
7: 0xdc4d19 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [node]
8: 0xdc5b55 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
9: 0xdc862c v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [node]
10: 0xd8f204 v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [node]
11: 0x10dc52e v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [node]
12: 0x145cc79 [node]
Aborted (core dumped)
所以我想即使在每次迭代中重新分配了dom
之后,先前的值仍将存储在堆中。为什么会这样,如何预防呢?
while(true)
循环会永远运行,因此您永远不会更改V8进行垃圾回收。由于许多其他原因,您永远不会在Javascript中进行永久运行的循环(除非循环中没有await
),因为nodejs是一个主要是单线程事件驱动的系统,并且在您忙于在[ C0]循环,无法处理任何事件,因此在nodejs中无法完成很多事情。
为什么会这样,如何防止呢?
垃圾回收器通常被设计为让您的代码运行并且不干扰其执行,然后,在您的代码不执行任何操作并且已完成其内部状态的瞬间,垃圾回收器将检查堆中对象的状态,以查找不再使用的对象。
您可以在不提供GC可以运行的任何空闲时间的情况下,避免陷入这种紧密循环,从而避免出现这种情况。如上所述,像这样的紧密循环会阻止nodejs中的任何其他事件继续运行,因此,出于其他原因,通常也是不好的原因。
注意,您的代码不仅创建了444个while(true)
实例以及与这些实例相关联的所有对象,而且没有提供清除创建该[]时使用的所有临时变量/字符串的任何功能。 C0]实例,在解析HTML时可能是实质性的,因此总内存使用量明显高于仅444个完成的dom
实例。如果您在循环中插入任何类型的呼吸室,则GC将有机会清理不再使用的东西。