2017-07-15 71 views
1

我想知道是否可以通過永不會恢復的異步函數創建內存泄漏。例如:使用異步方法瞭解Chrome GC

function timeout(ms) { 
    return new Promise(resolve => setTimeout(resolve, ms)); 
    } 

    function Test() { 
    this.arr = []; 

    this.alloc = async function() { 
     for (i = 0; i < 300000; i++) { 
     this.arr.push(document.createElement('div')); 
     } 
     await timeout(10000); 
     alert('done waiting ' + this.arr.length); // outputs 300000 
    }; 
    }; 

    var test = new Test(); 

    function leak() { 
    test.alloc(); 
    test = null; 
    window.gc(); // Running chrome with expose-gc flag 
    // Snapshotting memory here shows divs have been deallocated 
    } 

使用Chrome的內存工具,當這段代碼完成執行時,我抓住了一個快照。我預計會看到300000個HTMLDivElements仍然被分配,但是你會發現 - 內存似乎「被釋放」了。奇怪的是,如果我嘗試訪問它們仍然存在的數組內容。 任何人都可以解釋這種現象嗎?

+0

「expose-gc」標記記錄在哪裏? – guest271314

+0

如果gc行爲怪異並且受到'console.log'(或'alert')語句的影響或者不存在,我的猜測是'await'可以啓用相同的[範圍優化,閉包得到](https:/ /stackoverflow.com/questions/28388530/why-does-chrome-debugger-think-closed-local-variable-is-undefined)。 – Bergi

回答

1

如果我用一個定時器,取代someForeverPromise,然後試圖訪問數組的內容,他們還在那裏

someForeverPromise本身是垃圾收集,並用它所有仍在等待回調它 - 包括恢復async function

您可以使用

const foreverPendingPromise = new Promise(resolve => { 
    global.reference = resolve; 
}); 

在全球reference保持了一個方法來解決的承諾,以防止回調被當作垃圾收集。 (你也應該確保沒有人意外地呼叫reference以確保它保持待定狀態,但是我將把它作爲練習留給讀者)。

+0

這很奇怪,因爲如果我用計時器替換承諾並添加警報,它仍然顯示出來。 – Dandan

+0

@Dandan一個計時器確實保持其回調('resolve'?)的全局引用 – Bergi

+0

我使用了以下實現: 函數超時(ms){ 返回新的Promise(resolve => setTimeout(resolve,ms)) ; } 現在,如果我等待超時(10000),那麼事件探查器顯示內存已釋放,但我仍然得到警報。 – Dandan