内存泄露在前几年一直是个比较重要的话题,而如今虽然手机电脑内存容量较大,但依旧可能会存在严重的内存泄露问题。比如,一个 RN 界面存在内存泄露,一次访问回退,可能会导致占用内存增加 10M,以此不断切换页面,很快手机会出现卡顿的现象。
为什么 js 会发生内存泄露
这得先提到垃圾回收。线代浏览器都是自动回收垃圾,原理分两种:
- 引用计数垃圾回收
这一点和 objective-c 很像。当一个对象能访问另一个对象时,则意味着被访问的对象引用计数+1,当移除这种访问时,引用计数-1。当引用计数为 0 时,代表这此时作用域已经没有需要访问该对象的了,所以下一次垃圾回收会将该对象回收。
- 标记清除
该对象进入某个环境时,标记改变量为“进入环境”。从逻辑上讲,永远不能释放即进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。而当变量离开环境时,则将其标记为:离开环境。
除非以下情况:
全局变量,而且当前执行环境还没有被退出
一般来讲全局变量是开发者明确需要全局引用的,所以一般无需主动回收。除非,在某些特定情况,已经明确某些对象不再使用了,而且也占用较大内存的话,可以在组件卸载的时候将变量置为 null,这是浏览器会在下一次垃圾回收时将该变量回收。示例如下:
componentWillunmount() {
this.bigObject = null
}
闭包,闭包所储存的变量会延长它的生命周期
闭包往往是最容易出现内存泄露的地方
比如 setInterval 此类的定时器
经常出现的现象是组件已经卸载了,但是定时器还在跑,解决方案同样也是将定时器取消计时,示例如下:
componentDidMount() {
this.time = setInterval(() => {
this.now = new Date()
})
}
componentWillunmount() {
clearInterval(this.time)
}
addEventListener 添加的事件
添加的时间往往容易出现遗漏,当我们已经不需要该事件的时候,我们需要手动移除该监听,使用 removeEventListener
引用计数中循环引用
function problem() {
var objA = new Object()
var objB = new Object()
objA.someOtherObject = objB
objB.anotherObject = objA
}
办法是在页面卸载时手动将循环引用赋值为 null,比如:
objA.someOtherObject = null
objB.anotherObject = null
内存泄露调试技巧
react-native
safari 有一个调试工具,模拟器或者真机打开后,选择你的模拟器
点击工具栏的快照功能,此时等几秒就列表会出一个记录
此时再次操作手机或者模拟器,可以操作你认为可能出现内存泄露的路径,此时再点击工具栏的快照功能,出现新的一条记录后,即可对比两条记录的内存大小比较。
更多精彩文章可以看我的博客,如有错误,欢迎指正,共同进步