有JavaScript内存泄漏recursion调用callback函数?

比方说,例如,您正在编写一个程序,等待队列中的消息,对其进行处理,然后等待下一条消息,并且这个消息永远持续下去。 在像C或Java这样的语言中,它看起来像这样:

void processMessage() { while (true) { // waitForMessage blocks until the next message is received msg = waitForMessage(); // handle msg here } } 

在Javascript(我使用node.js,顺便说一句),因为使用callback,它通常看起来像这样:

 function processMessage() { waitForMessage(function(msg) { // handle msg or error here processMessage(); }); } 

我的担心是,你基本上有一个callback链recursion调用原来的function,这样做的开销可能会慢慢地消耗内存。 我猜这实际上不是一个问题,因为也许JavaScriptcallback独立存在于自己的堆栈上,而不是推到原始函数堆栈上? 有人向我解释了JavaScriptcallback和范围,并向我保证,在运行时间长达任意长时间的情况下,JavaScript代码不会耗尽内存,同时接收到任意大量的消息

不,recursion函数调用不会导致Javascript中的内存泄漏。

函数调用使用的唯一内存是一堆栈空间(所以解释器知道函数返回时该去哪里)以及函数的作用域对象(例如局部variables)使用的内存。 当函数调用返回时,堆栈内存已完成返回给系统。 它不泄漏。

在使用asynchronouscallback的JavaScript中,启动函数已经返回,因此在asynchronouscallback被调用之前堆栈已经被清除了,所以没有堆栈的build立。

内存中将有一个函数作用域对象,直到callback完成,这是重要的,并且需要允许内联callback访问其父范围中声明的variables。 只要callback完成(不再可及),该范围对象将被垃圾收集。 除非你正在做一些不寻常的事情,例如在那个临时范围内分配巨大的string或缓冲区,那么这个范围的内存使用情况就不成问题。

至于从一个初始函数调用中获取许多消息,然后重复调用同一个callback函数,请记住,父函数只执行一次,无论调用callback的次数多less,只分配一个作用域对象,所以没有每次调用callback时build立内存。 callback本身在每次被调用的时候都会得到一个新的函数作用域,但是由于callback函数本身并不触发任何asynchronous调用,所以这个作用域对象将是临时的,只要callback完成就可以进行垃圾回收工作和回报。

如果您将asynchronous操作链接在一起,则在asynchronous操作期间,将会保留其他范围对象,但这是Javascript的工作原理,并且提供了访问父范围的function。 在实践中,它通常不被certificate是一个记忆问题。 范围对象本身是相对紧凑的对象(几乎每个函数调用都创build一个对象),如上所述,只要不将巨大的缓冲区或巨大的string/数组放入持久范围,内存使用通常是不相关的。


另外请记住,当您从asynchronouscallback中再次调用processMessage()时,您可能通常会想到这种recursion,因为之前对processMessage()函数调用已经返回并且堆栈已经完全解除asynchronous事件触发了callback。 所以,在这种情况下没有堆积。 这是因为Javascript中的asynchronous操作都是通过一个事件队列来运行的。 当asynchronous操作准备好触发一个动作时,它将一个事件放入Javascript事件队列中。 该事件只在JS操作的当前线程完成并完全解开时处理。 只有这样,JS中间人才会在事件队列中查看是否还有别的事情要做。 因此,在下一个asynchronous操作触发之前,堆栈总是完全展开。

有关如何工作的更多信息以及JS事件队列(在node.js中的作用与浏览器中的作用相同)中的许多参考文章,请参阅此文章:

JavaScript如何在后台处理AJAX响应?

这是Joyent在node.js主页上将node.js称为“事件驱动的非阻塞I / O模型”的原因之一。

函数不在堆栈上分配。 callback函数在使用后将被垃圾回收,除非由于某种原因保留引用。 你的代码应该很好!

这里是伟大的说明毗邻nextTick()

 function processMessage() { waitForMessage(function(msg) { // handle msg or error here process.nextTick(processMessage; }); }