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文档, 最后才是群里问问

--- 牙叔教程


声明

部分内容来自网络

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

相关文章
|
iOS开发 编译器 JavaScript
iOS八种内存泄漏问题
循环引用(Retain Cycle) 先简单说一下什么是循环引用(retain cycle) ​假设我们有两个实例A和B,B是A的一个strong型的property,则B的引用计数是1,当A的需要释放的时候,A则会调用[B release]来释放B,B的引用计数则减为0,释放。
2038 0
|
3月前
|
XML NoSQL Linux
内存泄漏专题(3)内存泄漏调试神器valgrind
内存泄漏专题(3)内存泄漏调试神器valgrind
21 0
|
3月前
|
XML 存储 NoSQL
内存泄漏检测工具valgrind神器
内存泄漏检测工具valgrind神器
81 0
|
5月前
|
Android开发 iOS开发
[√]cocos2dx 内存泄露
[√]cocos2dx 内存泄露
39 0
|
7月前
|
存储 机器学习/深度学习 缓存
我惊了!!!ThreadLocal 源码存在内存泄露的 Bug!!!
我惊了!!!ThreadLocal 源码存在内存泄露的 Bug!!!
|
10月前
|
Linux Windows
逼格提升:内存泄漏检测工具
逼格提升:内存泄漏检测工具
|
11月前
|
Web App开发 JavaScript 前端开发
JavaScript 内存溢出解决办法 亲测有效
Vue3是一款非常流行的JavaScript框架,它被广泛应用于Web前端开发中。在使用Vue3进行打包时,有时会遇到以下错误信息:
|
Android开发
在安卓项目中使用 Leakcanary 内存泄露检测工具
使用 (一)导入 导入 Leakcanary-watcher、Leakcanary-analyzer、Leakcanary-android, 在当前项目的引用 Leakcanary-android 这个 library。
205 0
|
iOS开发
afnetworking 内存泄漏问题
afnetworking 内存泄漏问题
325 0
afnetworking 内存泄漏问题