自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Node.js SetTimeout 引起的內(nèi)存泄露問(wèn)題

開(kāi)發(fā) 前端
clearTimeout 中支持傳入 id 刪除定時(shí)器,而之前只支持傳入定時(shí)器對(duì)象。一切看起來(lái)沒(méi)問(wèn)題,但是實(shí)現(xiàn)這個(gè)特性的時(shí)候,忘了一種場(chǎng)景,那就是如果用戶(hù)沒(méi)有執(zhí)行 clearTimeout,而是定時(shí)器正常觸發(fā),因?yàn)樵诙〞r(shí)器正常觸發(fā)的邏輯中沒(méi)有刪除映射關(guān)系,從而導(dǎo)致了內(nèi)存泄露。

這是之前寫(xiě)的一篇文章,分享一下避免大家踩坑。

定時(shí)器回調(diào)通常會(huì)通過(guò)閉包持有外部的對(duì)象,比如下面的例子。

function demo() {
 const dummy = {}
 setTimeout(() => {
      dummy;
   }, 10000)
}

demo();

demo 執(zhí)行完后,demo 函數(shù)里的 dummy 對(duì)象是不會(huì)釋放的,因?yàn)樗€被 setTimeout 引用著,如果執(zhí)行很多次 demo 的話(huà),就會(huì)導(dǎo)致大量的內(nèi)存無(wú)法被釋放,直到執(zhí)行完 setTimeout,這通常不是什么問(wèn)題,除非 dummy 對(duì)象非常大。

但是如果是 setInterval 的話(huà),情況就不一樣了。

function demo() {
 const dummy = {}
 setInterval(() => {
      dummy;
   }, 10000)
}

demo();

上面的代碼會(huì)導(dǎo)致 dummy 永遠(yuǎn)不會(huì)被釋放,當(dāng)然這個(gè)例子很直接,大家并不會(huì)寫(xiě)出這樣的代碼,但是有時(shí)候代碼復(fù)雜的時(shí)候,就不好說(shuō)了,比如之前幫助業(yè)務(wù)排查問(wèn)題的時(shí)候經(jīng)常發(fā)現(xiàn) setInterval 導(dǎo)致的內(nèi)存泄露問(wèn)題,使用場(chǎng)景基本如下。

class Demo { 
    timer = null
    start() {
        this.timer = setInterval(() => {
            this;
        }, 10000)
    }
    stop() {
    // 通常會(huì)漏了這一句
    // clearInterval(this.timer);
    }
}

const demo = new Demo();
demo.start();
demo.stop();

所以使用 setInterval 的時(shí)候需要特別注意。

setInterval 導(dǎo)致內(nèi)存泄露很好理解,但是 setTimeout 導(dǎo)致的內(nèi)存泄露并不常見(jiàn),因?yàn)?setTimeout 執(zhí)行完后,相應(yīng)的內(nèi)存都會(huì)被釋放了。下面分享一個(gè)因?yàn)?Node.js Core 導(dǎo)致的 setTimeout 內(nèi)存泄露問(wèn)題,相關(guān) issue 可以參考這里。復(fù)現(xiàn)代碼如下。

for (i = 0; i < 500000; i++) {
   +setTimeout(() => {}, 0);
}

上面的代碼會(huì)導(dǎo)致 setTimeout 創(chuàng)建的 timer 對(duì)象無(wú)法釋放,乍一看,我們可以會(huì)被嚇到,這不就是我們平時(shí)的用法嗎?但是不用擔(dān)心,下面的例子并不會(huì)出現(xiàn)這個(gè)問(wèn)題。

for (i = 0; i < 500000; i++) {
 setTimeout(() => {}, 0);
}

仔細(xì)一看,有問(wèn)題的例子中 setTimeout 還有個(gè) + 號(hào),那么這個(gè)是做什么的呢?

這個(gè)還要說(shuō)起 setTimeout 在瀏覽器的實(shí)現(xiàn),在瀏覽器中,setTimeout 返回的是一個(gè) id,但是 Node.js 中返回的是一個(gè)對(duì)象,為了和瀏覽器兼容,Node.js 支持把返回的對(duì)象轉(zhuǎn)成 id,這個(gè) id 是定時(shí)器對(duì)應(yīng)的 async_hooks id,那么這個(gè)是怎么實(shí)現(xiàn)的呢?下面看一個(gè)例子。

const dummy = {
  [Symbol.toPrimitive]() {
   return 1
  }
};
console.log(+dummy)

上面例子會(huì)輸出 1,可以看到通過(guò) Symbol.toPrimitive 可以定義對(duì)象轉(zhuǎn)成原生類(lèi)型時(shí)的行為。下面是另一個(gè)例子。

const dummy = {
  [Symbol.toPrimitive]() {
    return "hello ";
  }
};
console.log(dummy + "world")

Node.js 正是利用這個(gè)能力實(shí)現(xiàn)了和瀏覽器的兼容,源碼如下。

Timeout.prototype[SymbolToPrimitive] = function() {
  const id = this[async_id_symbol];
  if (!this[kHasPrimitive]) {
    this[kHasPrimitive] = true;
    knownTimersById[id] = this;
  }
  return id;
};

所以一開(kāi)始那個(gè)例子中 +setTimeout 最終會(huì)執(zhí)行上面的代碼,從而拿到一個(gè) id。但是事情沒(méi)有那么簡(jiǎn)單,從上面的代碼中可以看到,除了返回一個(gè) id 外,還有另外一個(gè)邏輯,那就是把定時(shí)器對(duì)象保存到了一個(gè) map 中,其中 key 正是給用戶(hù)返回的 id,那么這個(gè)有什么用呢?看一下 clearTimeout 代碼。

function clearTimeout(timer) {
  if (typeof timer === 'number' || typeof timer === 'string') {
    const timerInstance = knownTimersById[timer];
    if (timerInstance !== undefined) {
      timerInstance._onTimeout = null;
      /*
         if (timerInstance[kHasPrimitive])
            delete knownTimersById[timerInstance[async_id_symbol]];
      */
      unenroll(timerInstance);
    }
  }
}

可以看到 clearTimeout 中支持傳入 id 刪除定時(shí)器,而之前只支持傳入定時(shí)器對(duì)象。一切看起來(lái)沒(méi)問(wèn)題,但是實(shí)現(xiàn)這個(gè)特性的時(shí)候,忘了一種場(chǎng)景,那就是如果用戶(hù)沒(méi)有執(zhí)行 clearTimeout,而是定時(shí)器正常觸發(fā),因?yàn)樵诙〞r(shí)器正常觸發(fā)的邏輯中沒(méi)有刪除映射關(guān)系,從而導(dǎo)致了內(nèi)存泄露。具體修復(fù)方案就是刪除這個(gè)映射關(guān)系就行,具體可以參考這個(gè) PR。

1. issue: https://github.com/nodejs/node/issues/53335.

2: pr: https://github.com/nodejs/node/pull/53337

責(zé)任編輯:姜華 來(lái)源: 編程雜技
相關(guān)推薦

2023-06-30 23:25:46

HTTP模塊內(nèi)存

2013-08-07 10:07:07

Handler內(nèi)存泄露

2017-03-19 16:40:28

漏洞Node.js內(nèi)存泄漏

2017-03-20 13:43:51

Node.js內(nèi)存泄漏

2022-01-02 06:55:08

Node.js ObjectWrapAddon

2020-01-03 16:04:10

Node.js內(nèi)存泄漏

2013-11-01 09:34:56

Node.js技術(shù)

2022-06-23 06:34:56

Node.js子線(xiàn)程

2015-01-14 13:50:58

AndroidHandler內(nèi)存泄露

2015-03-10 10:59:18

Node.js開(kāi)發(fā)指南基礎(chǔ)介紹

2020-05-29 15:33:28

Node.js框架JavaScript

2012-04-11 13:46:33

ibmdw

2021-12-25 22:29:57

Node.js 微任務(wù)處理事件循環(huán)

2012-02-03 09:25:39

Node.js

2011-09-09 14:23:13

Node.js

2011-11-01 10:30:36

Node.js

2011-09-08 13:46:14

node.js

2011-09-02 14:47:48

Node

2012-10-24 14:56:30

IBMdw

2011-11-10 08:55:00

Node.js
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)