轉陣列 警惕JS陣列解構轉引數導致爆棧問題

2021-10-14 14:39:07 字數 1739 閱讀 5506

起源是在寫webscoket服務的時候,發現開發工具偶爾報maximum call stack size exceeded的問題。由於當時沒時間,就草草把bug修復了,並未深究原因。現在覆盤工作的時候,又想起這個問題,於是再把這個問題拿出來研究。

出錯**大概是如下:

new array().push(...buffer.allocunsafe(2**17))

一開始看到超出呼叫棧以為是buffer的迭代器遞迴出現了問題,但事實並不是這樣。

由於出錯**是這樣的:

/*

* 而且存在臨界點,不同機器略有差異

* 本人的機器大於2**17就必報

*/new array().push(...buffer.allocunsafe(2**17))

當時認為是buffer的迭代器問題,所以就嘗試使用普通陣列

/*

* 這段**在瀏覽器也報錯

* 所以排除nodejs原因,應該是v8造成的

*/new array().push(...new array(2**17).fill(0xff))

發現普通陣列也存在類似問題,接下來就是排除迭代器

[...new array(2**17).fill(0xff)]

發現迭代居然沒有異常,難道是push方法?那就換個方法試試

console.log(...new array(2**17).fill(0xff))

居然log也報錯,那真相只有乙個,那就是引數超載

大家都知道,函式再呼叫函式的時候,是通過儲存在呼叫棧中來保持執行順序的,而棧是有一定大小,比如遞迴上數百萬次後也會出現爆棧。

那麼是否真的是因為棧不夠用了?還是說引數對呼叫棧也存在一些影響?

接下來我們就來逐一排查。首先確定是否是真的因為棧不夠用了

# 通過調整棧大小,來判斷是否是棧耗盡了,stack-size的單位是kb,預設是984

node --stack-size=2048 -e "new array().push(...buffer.allocunsafe(2**17))"

發現果然執行正常,所以可以確定是棧耗盡了

排查引數的數量對棧的影響

function recursiondepth(paramlen) 

try catch (err) ,最大深度則為:$`)

}}recursiondepth(2**4)

recursiondepth(2**8)

recursiondepth(2**12)

recursiondepth(2**16)

recursiondepth(2**20)

輸出結果:

當引數長度為16,最大深度則為:3489

當引數長度為256,最大深度則為:455

當引數長度為4096,最大深度則為:30

當引數長度為65536,最大深度則為:1

當引數長度為1048576,最大深度則為:0

所以由此確定引數的數量也是需要暫用呼叫棧的空間,而當引數長度達到足夠長,即使1幀也可以壓垮整個呼叫棧,超出呼叫棧空間。

java 陣列轉list list轉陣列

string strarray new string list list arrays.aslist strarray 注意 使用工具類 arrays.aslist 把陣列轉換成list時,不能使用其修改集合相關的方法,它的 add remove clear 方法會丟擲 unsupportedope...

js 陣列解構賦值

es6中新增了對陣列拆分並且賦值的方法 解構賦值 例子 let arr 1,2,3 let a,b,c arr console.log a a a 1 console.log b b b 2 console.log c c c 3等式左邊的 a,b,c 和右邊的陣列的元素相互對應,a arr 0 b...

PHP xml 轉陣列 陣列轉 xml 操作

path data.xml xml xml load file path foreach xml children as child xml load file 函式把 xml 文件載入物件中。file 必需。規定要使用的 xml 文件。class 可選。規定新物件的 class。options 可...