Android Raphael使用(专治native 内存泄漏)

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 命令行发送广播命令,从而执行一些逻辑操作。

猜你喜欢

转载自blog.csdn.net/hexingen/article/details/128714432