最近(并不是)收到社区反馈:win10打不开QQ小程序开发者工具,就是点击了没反应那种。这种问题比较麻烦,一般是需要去看crash report
的,但nwjs(实际上是谷歌的minidump_stackwalk)这块对window系统太不友好了,搞半天都搞不好。
这里发现另一个相同功能的,用rust写的:rust-minidump,安装倒是非常快,然并卵,它貌似只支持Firefox的minidump。。。
扯远了,总之看crash report这块没什么进展,只能怀着期望去nwjs的issue里找。
没想到真让我找到了:Nw.js sample app's window is not opened on Windows 10。
发现原来是缺少了vc++
的runtime:Visual C++ Redistributable。
然后和社区反馈用户沟通了下,安装了这个runtime之后真的正常了!
那么围绕着这个问题的版本升级就开始了。。。 找版本、解决兼容问题等略过。。。
我们来回归主题:内存泄漏。
在升级过程中明显感知到内存占用在上升,表现为调试面板时常崩溃,得重启。 但我以为这是常态,在生产环境下应该可以减少这方面的问题也就没理。
然而在构建安装包并安装之后。。。
一开始各方面测试还算正常,直到测试到小程序代码改动引起重新编译
这个功能点时,一两次触发之后就崩溃了!!!并且是稳定的崩溃,不是偶然性的。
这还得了,赶紧打开任务管理器看什么情况,然后就看到下面这个:
好家伙,这个线程吃的这么肥!
在重复测试了几次之后,确定是内存问题且是其中一部分有问题。那么结合前面调试面板多次
,可以大胆的怀疑是调试面板即devtools
的问题。
这里的调试面板指的是这个:
简单的说下这个devtools:
我们今天的主角就来自于我们注入的代码。
虽然前面我们通过任务管理器看到内存在膨胀,但可以的话我们还是专业点,通过performance来分析下内存泄漏。
这块很简单,因为我们明确引起内存膨胀的行为有点击【编译】按钮触发重新编译,所以我们只需要记录连续多次
从点击按钮到编译完成这个过程即可。
可以清晰的看到内存并没有被释放,且通过帧
和截图
两栏可以肯定上升时正好是编译的阶段。
对比下以前的版本:
可以发现内存是正常释放的。
performance很好用,但是它主要是用来分析代码耗时之类的性能问题,对于内存泄漏就有些不对口了。所以我们需要借助另一个工具:memory。
这里用先用堆快照
看下啥玩意儿吃我这么多内存:
发现是devtools-frontend
提供的sdk
里的一段代码。
坏了,不会是devtools-frontend的问题吧?!是的话这次升级得回炉重造了!
然而逛遍GitHub也没找到相关issue,而且这次升级改动也主要是sdk相关的,貌似对方从cjs
重构成esm
了,所以有些东西它不往外暴露,需要我们通过其它手段去获取对应的api
,所以有理由怀疑是代码导致的(实际上我还试了其它nw的版本,发现超过本次升级版本的都是这样)。
跟着快照给出的索引
,我们在这里打上断点,看下是啥代码引起的创建这个对象:
然后重新编译:
嗯?!蛇皮调用栈一段带一段的,一看就不对劲。 相关代码:
递归等待拿到sdk用来手动开启network的disabled cache
。
但理论上不会这么长,顶多一两次,然后。。。
原来是函数里报错导致一直被catch!!!
catch没有输出error,导致一直以为这里是正常的。。
孔子言:try而不catch(指不输出error),坏人也。
那么问题是否真的出在这呢?我们修复看看
其实这里还有个问题,就是bug引起创建的对象为什么没有被回收,实际上这个对象还被内部其它对象引用,所以导致了不会释放,这个也是可以通过memory快照里看到的。
这里也是api的调整导致的报错,所以在devtools-frontend里找到相关的api替换上即可,这里就不贴代码了。
修复后发现内存回收正常了:
这种bug处理起来还是很有成就感的,虽然只是一个小小的内存泄漏。devtools的几个面板,关键时刻真能救人!