一次脚本测试的内存增长问题

问题背景

问题描述:进入应用的视频素材剪辑页面然后退出,脚本循环执行500次,内存增长156M

问题分析

分析增长曲线图

一次脚本测试的内存增长问题_第1张图片

曲线反映了从0到500次脚本执行过程中adb shell dumpsys meminfo抓取内存的增长情况,可以看出是Native内存一直增长未释放。

Profiler工具分析

将执行100的hprof和500次的hprof文件分别导入Android Studio的Profiler中。
一次脚本测试的内存增长问题_第2张图片
一次脚本测试的内存增长问题_第3张图片
可以看到“0 Leaks”,以及Native Size只增长了200k左右。至此,Profiler体现不出内存增长的原因。

MAT工具分析

hprof格式转换

利用sdk\platform-tools中的hprof-conv.exe将hprof转换为MAT可识别的文件

hprof对比

用MAT打开100次和500次的hprof文件,然后进行对比两份hprof
一次脚本测试的内存增长问题_第4张图片
可以看到WeakReference、ViewRootImpl.W、Cleaner和NativeAllocationRegistry.CleanerThunk的对象数量增长比较多。

分析ViewRootImpl.W

查看ViewRootImpl.W发现是一个和Window相关的Binder接口
一次脚本测试的内存增长问题_第5张图片
查看ViewRootImpl.W的引用关系,
一次脚本测试的内存增长问题_第6张图片
发现是被Cleaner所持有,得出ViewRootImpl.W增长是因为Cleaner在增长。

分析Cleaner

查看源码

public class Cleaner extends PhantomReference<Object>
{

    // Dummy reference queue, needed because the PhantomReference constructor
    // insists that we pass a queue.  Nothing will ever be placed on this queue
    // since the reference handler invokes cleaners explicitly.
    //
    private static final ReferenceQueue<Object> dummyQueue = new ReferenceQueue<>();

    // Doubly-linked list of live cleaners, which prevents the cleaners
    // themselves from being GC'd before their referents
    //
    static private Cleaner first = null;

    private Cleaner
        next = null,
        prev = null;

    private static synchronized Cleaner add(Cleaner cl) {
        if (first != null) {
            cl.next = first;
            first.prev = cl;
        }
        first = cl;
        return cl;
    }

    private static synchronized boolean remove(Cleaner cl) {

        // If already removed, do nothing
        if (cl.next == cl)
            return false;

        // Update list
        if (first == cl) {
            if (cl.next != null)
                first = cl.next;
            else
                first = cl.prev;
        }
        if (cl.next != null)
            cl.next.prev = cl.prev;
        if (cl.prev != null)
            cl.prev.next = cl.next;

        // Indicate removal by pointing the cleaner to itself
        cl.next = cl;
        cl.prev = cl;
        return true;

    }

    private final Runnable thunk;

    private Cleaner(Object referent, Runnable thunk) {
        super(referent, dummyQueue);
        this.thunk = thunk;
    }

    /**
     * Creates a new cleaner.
     *
     * @param  ob the referent object to be cleaned
     * @param  thunk
     *         The cleanup code to be run when the cleaner is invoked.  The
     *         cleanup code is run directly from the reference-handler thread,
     *         so it should be as simple and straightforward as possible.
     *
     * @return  The new cleaner
     */
    public static Cleaner create(Object ob, Runnable thunk) {
        if (thunk == null)
            return null;
        return add(new Cleaner(ob, thunk));
    }

    /**
     * Runs this cleaner, if it has not been run before.
     */
    public void clean() {
        if (!remove(this))
            return;
        try {
            thunk.run();
        } catch (final Throwable x) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        if (System.err != null)
                            new Error("Cleaner terminated abnormally", x)
                                .printStackTrace();
                        System.exit(1);
                        return null;
                    }});
        }
    }

}

查看引用关系
一次脚本测试的内存增长问题_第7张图片
可以看到Cleaner类是链表结构,不再好继续往下分析了。

分析WeakReference

从上面ViewRoomImpl.W源码可以看到有WeakReference类型的成员变量,推断WeakReference增长832次有400次是因为ViewRootImpl.W增长400次。用MAT合并虚引用、软引用和弱引用
一次脚本测试的内存增长问题_第8张图片
剩余没有被过滤的133个对象,查看存放的值有很多和音视频播放相关
一次脚本测试的内存增长问题_第9张图片
一次脚本测试的内存增长问题_第10张图片
一次脚本测试的内存增长问题_第11张图片

反馈给火山SDK

屏蔽视频播放功能

视频素材剪辑页面主要有视频抽帧和视频播放功能两个功能,结合上面WeakReference的分析,怀疑视频播放功能产生了Native的泄露。删除播放器的初始化,跑脚本复测后发现内存平稳。

问题解决

火山同事发现是so中shared_ptr循环引用导致,更新版本后解决。

你可能感兴趣的:(android,内存增长)