Android Raphael使用(专治native 内存泄漏)
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
1.前期准备
在项目根目录build.gradle
中,添加仓库地址
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
2.案例实践
构建一个新的Library Module其中build.gradle
中添加依赖
dependencies {
implementation 'com.github.bytedance:memory-leak-detector:0.2.1'
}
Raphael API使用
Raphael是有两种方式可以监控进程中native内存 其一是通过java代码来实现其二是通过adb shell 发送广播命令行来控制。
启动监控
// 监控指定的so
Raphael.start(
Raphael.MAP64_MODE|Raphael.ALLOC_MODE|0x0F0000|1024,
"/storage/emulated/0/raphael", // 需要申请读写sdcard权限
null
);
参数
- 第一个参数 指定模式
- 第二个参数 native内存文件存放的目录若是sdcard则需要申请权限
- 第三个参数指定监控的so库。比如监控libxxx.so中内存,则传入
".*libxxx\\.so$"
; 若传入null 则监控进程中全部so库
等同于adb shell 命令行实现
## 监控整个进程RaphaelReceiver 组件所在的进程
## 0x0CF0400=Raphael.MAP64_MODE|Raphael.ALLOC_MODE|0x0F0000|1024
adb shell am broadcast -a com.bytedance.raphael.ACTION_START -f 0x01000000 --es configs 0xCF0400
打印内存
// 代码控制
Raphael.print();
等同于
## 本地广播
adb shell am broadcast -a com.bytedance.raphael.ACTION_PRINT -f 0x01000000
更多API详情请阅读Raphael API使用
实现思路
为了灵活使用建议使用两者配合使用首先通过java 代码方式 在Application中启动监控整个进程其次涉及业务场景后通过adb shell 来打印内存缓存到指定目录下。
代码实现
基于raphael api 简单封装下代码如下
public class RaphaelUtils {
/**
* 监听整个进程中全部的so库内存泄漏
*/
public static void monitorAllNativeSo(){
String regexSo=null;
monitorNativeSo(regexSo);
}
/**
* 监控指定的 so 库内存泄漏
* @param regexSo
*/
public static void monitorNativeSo(String regexSo){
final String spaceDir="/storage/emulated/0/raphael";
monitorNativeSo(spaceDir,regexSo);
}
/**
* 用于监听so内存泄漏存储到指定位置
* 也可以通过adb shell 命令行来执行(灵活使用)
* adb shell am broadcast -a com.bytedance.raphael.ACTION_START -f 0x01000000 --es configs 0xCF0400 --es regex ".*libXXX\\.so$"
*
*
* @param spaceDir 记录泄漏的存放地址这里必须获取 读写权限。
* 比如"/storage/emulated/0/raphael"
* @param regexSo 传入null 监听全部so库。
*/
public static void monitorNativeSo(String spaceDir,String regexSo){
// 监控整个进程
Raphael.start(Raphael.MAP64_MODE|Raphael.ALLOC_MODE|0x0F0000|1024,
spaceDir,
regexSo
);
}
/**
* 打印内存泄漏的信息存储导致sdcard中
*
* 也可以通过adb shell 命令行来执行(灵活使用)
* adb shell am broadcast -a com.bytedance.raphael.ACTION_PRINT -f 0x01000000
*/
public static void printNativeLeak(){
Raphael.print();
}
/**
* 停止监听
* 也可以通过adb shell 命令行来执行(灵活使用)
* adb shell am broadcast -a com.bytedance.raphael.ACTION_STOP -f 0x01000000
*/
public static void stopMonitor(){
Raphael.stop();
}
}
在Application中onCreate()指定监控so库
public void onCreate() {
super.onCreate();
RaphaelUtils.monitorNativeSo( ".*libAppPlayx\\.so$");
}
构建apk 进行安装启动后赋予读写权限进行一些列的业务操作进行adb shell 命令操作打印内存情况
adb shell am broadcast -a com.bytedance.raphael.ACTION_PRINT -f 0x01000000
内存文件将缓存在Raphael.start()中第二个传入的目录下这里是"/storage/emulated/0/raphael"
将其拷贝出来通过adb pull 也来拷贝。
配置Python 环境
report 文件是无法直接进行获取native 堆栈信息需要借用raphael.py 进行转换。先配置python的环境在官网下载python 3.x ,进行默认安装。
在命令中执行python 检查是否安装成功
为了方便将需要的几个文件都拷贝到同一个目录中
- so对应addr2line工具
- 带有符号表的so库
- report文件
- py脚本(raphael.py/mmap.py)
执行命令行如下若是report 过大执行会过长
执行完后会在同个目录中生成leak-doublets.txt里面包含内存信息和堆栈信息。
打开默认生成leak-doublets.txt:
204,364,751 totals // 单指raphael拦截到的未释放的虚拟内存总和
204,364,751 libAppPlayJNI.so //该库未释放的内存
0x0000007865600000, 58741400, 1
0x0000000001171d4c /data/app/~~vZjWn3Y0HAlsqNnxySQWjg==/com.xxx.miniworld-J6zXyou6Nnd_y7pG4EZo7g==/lib/arm64/libAppxxxJNI.so (F:/minichina/miniad/xxxx/OgreSingleton.h:110)
....
信息解读
内存和调用次数
0x0000007865600000, 58741400, 1
0x0000007865600000
是report里此堆栈第一次分配出的内存地址58741400
是report里此堆栈的内存总和 1
是report里此堆栈的总次数
native的调用栈
0x0000000001171d4c /data/app/~~vZjWn3Y0HAlsqNnxySQWjg==/com.xxx.miniworld-J6zXyou6Nnd_y7pG4EZo7g==/lib/arm64/libAppxxxJNI.so (F:/minichina/miniad/xxxx/OgreSingleton.h:110)
根据以上信息就很好分析哪些native 对象占用内存在某些业务场景下该进行释放。
3.借鉴
raphael其中有一个很好的借鉴方面通过adb shell 命令行发送广播命令从而执行一些逻辑操作。