无极重试模式内存占用(Promise Retry Pattern Memory Footprint)

2019-09-26 03:57发布

考虑一个功能,使一个HTTP请求,直到成功为止:

function fetch_retry(url) {
    return fetch(url).catch(function(error) {
        return fetch_retry(url);
    });
}

由于故障数量的增加,确实内存消耗增加?

我想调用堆栈高度为O(1),但我不知道是否Clojure的情况下(随着时间而长势)被保留。


编辑:

案例2

function fetch_retry(url) {
    return fetch(url).catch(function(error) {
      //return fetch_retry(url)+1;
        return fetch_retry(url).then(res=>{res.body+='a'});
    });
}

(假设取解析为一个数字)的常数1应该在存储器,因为它是在以后使用。

情形3

function fetch_retry(url, odd_even) {
    return fetch(url).catch(function(error) {
      //return fetch_retry(url, 1-odd_even)+odd_even;
        return fetch_retry(url, 1-odd_even).then(res=>{res.body+=str(odd_even)});
    });
}

fetch_retry('http://..', 0)

通过交替0和1,编译器不能重新使用的操作数。


编辑:

有关情况2和3,没有人解释,所以我跑了实验。 令人惊讶的是增加内存中的所有场景。

function f(n) {
    return new Promise(function (resolve, reject) {
        gc();
        if(n==10000) return resolve(0);
        // else return f(n+1).then(value=>value+n);
        // else return f(n+1).then(value=>value);
        else return resolve(f(n+1));
    });
}

f(0).then(res => {
    console.log('result: ', res);
    const used = process.memoryUsage().heapUsed / 1024 / 1024;
    console.log(`memory: ${Math.round(used * 100) / 100} MB`);
}).finally(()=>console.log('finished'));

并与运行

node --stack_size=999999 --expose_gc test.js

请注意,我跑GC()每到防止迟发性GC时间。

  • 其中n = 1000,存储器:4.34 MB
  • 其中n = 10000,存储器:8.95 MB

5MB 9000个堆栈=每呼叫590个字节。

一种可能性是, resolve在每个堆叠功能被保留。 要删除此,

function f(n) {
    return new Promise(function (resolve, reject) {
        gc();
        if(n==10000) resolve(0);
        // else return f(n+1).then(value=>value+n);
        else return f(n+1).then(value=>value);
        // else return f(n+1);

        const used = process.memoryUsage().heapUsed / 1024 / 1024;
        console.log(`memory: ${Math.round(used * 100) / 100} MB`);
    });
}
f(0);
  • 其中n = 1000,存储器:4.12 MB
  • 其中n = 10000,存储器:7.07 MB

所以堆栈平坦,但因为我们认为记忆是不干净?

Answer 1:

由于故障数量的增加,确实内存消耗增加?

事实并非如此 。 承诺的现代实现没有他们源自承诺的记忆。 我可以确认这是火狐,Chrome,Safari和边缘的情况。

“老”的实现了这个问题(认为Q)。 现代浏览器和快速的库,例如蓝鸟“分离”解决后的参考。

这就是说,没有理由现在递归来实现这一点,你可以做:

async function fetch_retry(url) {
  while(true) { // or limit retries somehow
    try {
      await fetch(url);
    } catch { }
  }
}

需要注意的是重试永远是一个主意,我热烈推荐是友好的后端,使重试和限制重试的量在使用指数退避。

警告

这实际上是很难从你的最终测量,因为当调试器V8(Chrome的JavaScript引擎)异步堆栈跟踪信息收集和你的递归例如内存增长附后。

您可以关闭异步堆栈从Chrome devtools痕迹(有一个复选框)。

这将泄露一点点(堆栈帧)的发展,但尚未投入生产。

其他注意事项

如果fetch本身泄漏的错误(它已在时间对于某些类型的请求之前完成)内存,则代码将明显泄漏。 据我所知固定发现泄漏常绿所有浏览器,但有可能是一个在未来。



Answer 2:

return fetch(url).catch(function(error) {
    return fetch_retry(url);
    // Function exits, losing reference to closure
    // Closure gets garabge collected soon
});

当回调( catch )接到电话,就会失去了参考逮住的功能,因此该功能关闭将得到carbage收集。 只有外承诺,当前存在未决的住宿之一。

由于故障数量的增加,确实内存消耗增加?

没有。



文章来源: Promise Retry Pattern Memory Footprint