【小程序代码自查】【内存泄露】注册组件对象使用闭包函数导致
发布于 3 年前 作者 sqin 1152 次浏览 来自 分享

背景

经过上一次排查了内存泄露之后,内存泄漏的问题好了很多。但是最近有用户反馈还是会有内存告警的问题,所以有可能会存在内存泄露,经过排查,确实存在内存泄露,但是内存泄露的原因不明,并非是在延时调用页面实例对象。

如何确定问题

使用方法

二分debugger + 在内存中查找对象最短引用路径

具体操作

  1. 找到页面开始调用的生命周期(onload),将生命周期内部调用的方法全部注释。查看页面实例是否会被回收。
  2. 如果没被回收,则注释下一个生命周期执行的代码。
  3. 如果页面实例被回收,则将生命周期内部调用的方法注释一半。
  4. 重复123 直到最小的一行代码。

通过二分debugger法,查找到是调用了this.setData({a: “xxxxx”}),才会导致内存泄露的。
所以我猜测是因为有组件没被回收,才导致的页面实例没被回收,因为组件会存在对页面实例的引用。
查找setData对应的属性值,找到影响的组件,看下是否有监听页面属性的操作,如果有监听的操作,查看监听操作内部做了什么逻辑,然后再通过二分debugger,找到真正影响的代码。比如下图的示例代码中的:
this.cData[onlyKey] = new Move(this)
按道理,页面被销毁,自己的cData 属性也会被销毁才对,就算存在循环应用也没什么问题。在不了解底层逻辑情况下,我通过查找组件实例的最短引用路径,发现组件实例存在于底层逻辑的闭包函数中。如下实例的:

// 处理原有的自定义属性,等组件创建再把自定义属性放到组件上
function handleCompObj(compObj){
// xxxxxxxx
}

最终发现底层逻辑有对于针对自定义属性做缓存,相当于全部组件实例都共用一份自定义属性。

下面是示例代码:

// components/comp.js
console.log("comp");
class Move{
  constructor(config){
    this.config = config
  }
}
let numId = 0
// 组件对象类
class Comp {
  cData = {}
  constructor(){
  }
  created = function(){
    this.key = "comp" + numId++
    // 创建唯一key
    const onlyKey = "onlyKey" + new Date()
    // 用组件自定义属性存储 一个带有组件实例的对象,存在循环引用
    this.cData[onlyKey] = new Move(this)
    console.log("this.cData", this.cData)
  }
  /**
   * 组件的初始数据
   */
  data = {
  }
}
const compObj = new Comp()
console.log("compObj", compObj);

// 处理原有的自定义属性,等组件创建再把自定义属性放到组件上
function handleCompObj(compObj){
  // 自定义属性
  const customPropties = {
  }
  const compPropertyKeys= [
    "externalClasses",
    "behaviors",
    "relations",
    "data",
    "properties",
    "methods",
    "lifetimes",
    "pageLifetimes",
    "definitionFilter",
    "options",
    "setData",
    "observers",
  ]
  Object.keys(compObj).map((key)=>{
    // 固定属性排除
    if(compPropertyKeys.includes(key)){return}
    customPropties[key] = compObj[key]
  })
  // 原有组件创建生命周期
  const originCreated = compObj.created
  function newCreated(){
    // 组件创建的时候,将自定义属性赋给组件实例对象
    Object.assign(this, customPropties)
    // 运行原有的组件逻辑
    originCreated.call(this)
  }
  compObj.created = newCreated
  return compObj
}
// 处理组件类的实例,给组件增加自定义属性
const newCompObj = handleCompObj(compObj)
// 注册组件
Component(newCompObj)

解决办法

在给组件增加自定义属性的时候,如果是对象,则需要深复制。

回到顶部