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

--- 牙叔教程


声明

部分内容来自网络

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

相关文章
|
Java Android开发
OOM内存泄露速查备忘录
本文整理了一份OOM内存泄露问题速查备忘录,详细见下文。
OOM内存泄露速查备忘录
|
Java API Android开发
浅谈LeakCanary2源码(二)
浅谈LeakCanary2源码(二)
46 0
|
1月前
|
监控 算法 iOS开发
深入探索iOS函数调用栈:符号化与性能调优实战
在iOS开发中,理解函数调用栈对于性能调优和问题排查至关重要。函数调用栈记录了程序执行过程中的函数调用顺序,通过分析调用栈,我们可以识别性能瓶颈和潜在的代码问题。本文将分享iOS函数调用栈的基本概念、符号化过程以及如何利用调用栈进行性能调优。
38 2
|
7月前
|
算法 编译器 C语言
【C/C++ 实用工具】内存泄漏与堆溢出检测工具一览
【C/C++ 实用工具】内存泄漏与堆溢出检测工具一览
407 0
|
7月前
|
XML NoSQL Linux
内存泄漏专题(3)内存泄漏调试神器valgrind
内存泄漏专题(3)内存泄漏调试神器valgrind
121 0
|
7月前
|
XML 存储 NoSQL
内存泄漏检测工具valgrind神器
内存泄漏检测工具valgrind神器
140 0
|
缓存 Android开发 C++
[√]Android平台ParticleSystem内存泄露的排查过程
[√]Android平台ParticleSystem内存泄露的排查过程
85 1
|
Android开发 iOS开发
[√]cocos2dx 内存泄露
[√]cocos2dx 内存泄露
80 0
|
存储 Java API
浅谈LeakCanary2源码(一)
浅谈LeakCanary2源码
66 0
IDEA多线程下空指针断点调试
IDEA多线程下空指针断点调试
127 1
IDEA多线程下空指针断点调试