开发者社区> 小凡晓宇> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

autojs之内存泄露

简介: 内存泄漏的概念
+关注继续查看

内存泄漏的概念


百度百科:

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。


media projection泄露

和群里的@新城 看了截图内存泄漏,发现漏的不是aj,是系统进程 ,

一开media projection就开始涨内存 ,

没找到规避方法 除非用root截屏 ,

安卓的bug,雷电也休不了呀 ,

这个是合成surface的,本来就有这个进程

autojs版本

9.0.5

autojs官方文档自带示例

// autojs官网: https://pro.autojs.org/docs/#/zh-cn/debug
$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();

for (let i = 0; i < 10; i++) {
  // 这个图片本应手动调用recycle回收
  let leak = captureScreen().clone();

  // 我们故意注释掉回收的代码
  // leak.recycle();
}


autojs开发者定的规矩


图片相关代码
除了截图不用主动回收图片,
其他图片处理, 一律必须主动回收
内存泄露只检测Image对象,不检测Mat

img.recycle();


截图不会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();

for (let i = 0; i < 10; i++) {
  img = captureScreen();
  sleep(10);
  img = captureScreen();
  sleep(10);
  img = captureScreen();
  sleep(10);
  img = captureScreen();
}


找色不会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
if (!requestScreenCapture()) {
  toast("请求截图失败");
  exit();
}
var img = captureScreen();
toastLog("开始找色");
//指定在位置(100, 220)宽高为400*400的区域找色。
//#75438a是编辑器默认主题的棕红色字体(数字)颜色,位置大约在第5行的"2000",坐标大约为(283, 465)
var point = findColorInRegion(img, "#75438a", 90, 220, 900, 1000);
if (point) {
  toastLog("x = " + point.x + ", y = " + point.y);
} else {
  toastLog("没有找到");
}


找图, 会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();

var bigImg = captureScreen();
var smallImg = images.clip(bigImg, 10, 10, 200, 200);
var point = findImage(bigImg, smallImg);
toastLog(point);
// 不主动回收, 就会内存泄露
// bigImg.recycle();
// smallImg.recycle();


灰度化图片, 会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();

var img = captureScreen();
let grayImg = images.grayscale(img); // 灰度化图片
// 不主动回收, 就会内存泄露
// grayImg.recycle();


嵌套函数处理图片, 会内存泄露

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();

let img = captureScreen();
let thresholdImg = images.threshold(images.grayscale(img), 100, 200);
// 有两张图片, 你只回收了一张
thresholdImg.recycle();


正确的做法: 不要嵌套函数处理图片

$debug.setMemoryLeakDetectionEnabled(true);
requestScreenCapture();

let img = captureScreen();
let grayscaleImg = images.grayscale(img);
let thresholdImg = images.threshold(grayscaleImg, 100, 200);
grayscaleImg.recycle();
thresholdImg.recycle();


其他泄露情况

mat

// mat是opencv常用的数据类型, 需要主动释放
// 请求截图
if (!requestScreenCapture()) {
  toast("请求截图失败");
  exit();
}
let img = captureScreen();
let mat = img.getMat();
// 释放mat资源
mat.release();


ArrayList

let arrayList = new java.util.ArrayList();
// 用完以后, 及时释放资源
arrayList.clear();
arrayList.trimToSize();
arrayList = null;


events.broadcast

"ui";
切换界面1();
function 切换界面1() {
  ui.layout(
    <vertical>
      <text textSize="66sp">界面1</text>
      <button id="btn1">按钮1</button>
    </vertical>
  );

  ui.btn1.click(function () {
    log("切换界面2");
    切换界面2();
  });
  log("界面1显示出来了");
  events.broadcast.emit("界面名称", "界面1");
  events.broadcast.on("界面名称", function (name) {
    log("界面名称: " + name);
  });
}

function 切换界面2() {
  ui.layout(
    <vertical>
      <text textSize="66sp">界面2</text>
      <button id="btn2">按钮2</button>
    </vertical>
  );
  ui.btn2.click(function () {
    log("切换界面1");
    切换界面1();
  });
  log("界面2显示出来了");
  events.broadcast.emit("界面名称", "界面2");
  events.broadcast.on("界面名称", function (name) {
    log("界面名称: " + name);
  });
}


日志如下

09-06 08:49:37.177 main/V: 开始运行[remote://main.js] 
09-06 08:49:37.250 main/D: 界面1显示出来了 
09-06 08:49:37.261 main/D: 界面名称: 界面1 
09-06 08:49:38.826 main/D: 切换界面2 
09-06 08:49:38.831 main/D: 界面2显示出来了 
09-06 08:49:38.837 main/D: 界面名称: 界面2 
09-06 08:49:38.837 main/D: 界面名称: 界面2 
09-06 08:49:39.977 main/D: 切换界面1 
09-06 08:49:39.991 main/D: 界面1显示出来了 
09-06 08:49:39.994 main/D: 界面名称: 界面1 
09-06 08:49:39.995 main/D: 界面名称: 界面1 
09-06 08:49:39.996 main/D: 界面名称: 界面1 
09-06 08:49:41.565 main/D: 切换界面2 
09-06 08:49:41.579 main/D: 界面2显示出来了 
09-06 08:49:41.583 main/D: 界面名称: 界面2 
09-06 08:49:41.584 main/D: 界面名称: 界面2 
09-06 08:49:41.585 main/D: 界面名称: 界面2 
09-06 08:49:41.586 main/D: 界面名称: 界面2 
09-06 08:49:42.872 main/D: 切换界面1 
09-06 08:49:42.890 main/D: 界面1显示出来了 
09-06 08:49:42.897 main/D: 界面名称: 界面1 
09-06 08:49:42.901 main/D: 界面名称: 界面1 
09-06 08:49:42.902 main/D: 界面名称: 界面1 
09-06 08:49:42.902 main/D: 界面名称: 界面1 
09-06 08:49:42.903 main/D: 界面名称: 界面1 


总结
函数每执行一次, 就注册一次广播events.broadcast, 所以不应当在函数中注册广播, 除非你及时释放广播监听

释放指定的广播监听

"ui";
let listener1;
let listener2;
切换界面1();

function 切换界面1() {
  if (listener2) {
    events.broadcast.removeListener("界面名称", listener2);
  }

  ui.layout(
    <vertical>
      <text textSize="66sp">界面1</text>
      <button id="btn1">按钮1</button>
    </vertical>
  );
  ui.btn1.click(function () {
    log("切换界面2");
    切换界面2();
  });
  log("界面1显示出来了");
  events.broadcast.on("界面名称", function (name) {
    log("界面名称: " + name);
  });
  events.broadcast.emit("界面名称", "界面1");
  listener1 = events.broadcast.listeners("界面名称")[0];
}

function 切换界面2() {
  if (listener1) {
    events.broadcast.removeListener("界面名称", listener1);
  }
  ui.layout(
    <vertical>
      <text textSize="66sp">界面2</text>
      <button id="btn2">按钮2</button>
    </vertical>
  );
  ui.btn2.click(function () {
    log("切换界面1");
    切换界面1();
  });
  log("界面2显示出来了");
  events.broadcast.on("界面名称", function (name) {
    log("界面名称: " + name);
  });
  events.broadcast.emit("界面名称", "界面2");
  listener2 = events.broadcast.listeners("界面名称")[0];
}


animator不移除监听会内存泄漏

设置animator监听

let animator = ValueAnimator.ofInt(255, 100);
animator.setDuration(animatorDuration); //动画时间
// setRepeatMode(int value)用于设置循环模式,取值为ValueAnimation.RESTART时,表示正序重新开始,当取值为ValueAnimation.REVERSE表示倒序重新开始。
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.addListener(
  new AnimatorListenerAdapter({
    onAnimationStart: function (animation) {
      log("animation start");
    },
    onAnimationEnd: function (animation) {
      log("animation end");
    },
    onAnimationRepeat: function (animation) {
      log("animation repeat");
      // themeColor = rndColor();
    },
    onAnimationCancel: function (animation) {
      log("animation cancel");
      // themeColor = rndColor();
    },
  })
);
animator.addUpdateListener(
  new ValueAnimator.AnimatorUpdateListener({
    onAnimationUpdate: function (valueAnimator) {
      let value = valueAnimator.getAnimatedValue();
      setGradientBorder(value, themeColor);
    },
  })
);


移除animator监听

events.on("exit", function () {
  log("exit start");
  animator.removeAllUpdateListeners();
  animator.removeAllListeners();
  animator.cancel();
  animator = null;
  w.close();
  log("exit end");
});


参考

JavaScript中的垃圾回收和内存泄漏

名人名言

思路是最重要的, 其他的百度, bing, stackoverflow, 安卓文档, autojs文档, 最后才是群里问问

--- 牙叔教程


声明

部分内容来自网络

本教程仅用于学习, 禁止用于其他用途

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
autojs红眼特效
牙叔教程 简单易学
27 0
autojs起手式
牙叔教程 简单易懂
36 0
autojs之权限
使用场景 申请各种权限, 以及一些坑
226 0
autojs音乐动效
牙叔教程 简单易懂
37 0
autojs清空日志
牙叔教程 简单易懂
43 0
autojs之通信
通信概念 两个主体, 相互之间交换信息
57 0
autojs之lua
使用场景 在autojs中使用lua
58 0
autojs下拉刷新
牙叔教程 简单易懂
139 0
autojs读取指定行
autojs读取指定行
129 0
使用MAT分析内存泄露
使用MAT分析内存泄露 对于大型服务端应用程序来说,有些内存泄露问题很难在测试阶段发现,此时就需要分析JVM Heap Dump文件来找出问题。随着单机内存越来越大,应用heap也开得越来越大,动辄十几G的Dump也不足为奇了。
1500 0
+关注
332
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载