美文网首页
Android内存泄漏解决流程

Android内存泄漏解决流程

作者: 巴黎没有摩天轮Li | 来源:发表于2020-11-04 14:45 被阅读0次

    前言

    “A small leak will sink a great ship.” - Benjamin Franklin

    Step1 - Find Leak

    我们在日常开发的时候,除了自己在编写代码的时候注意,但未免还是会出现泄漏的情况,可见一个自动捕捉内存泄漏的重要性。

    Tools - LeakCanary
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
    

    ok,一行代码解决,初始化时通过ContentProvider,并且时在Debug才会生效。

    Tools - MAT

    mat相比于LeakCanary稍微麻烦,并且没有后者的UI提醒展示,需要自己去发掘定位点,不过mat提供了相对醒目的堆信息分配饼图展示。


    image.png

    首先,将堆转储一下,将.hprof文件保存一下,这时候的.hprof文件并不能被mat打开,
    而是需要将文件通过Android SDK中的platform-tools 中的hprof-conv脚本转一下。


    命令转
    mat
    正则匹配泄漏class
    找到该类被谁引用
    找到引用的类的GCRoot 弱引用等除外
    泄漏点找到了

    案例分析

    我司用了车牌扫描的OCR,实则是第三方SDK造成的内存泄漏,不过这边我记录下分析过程。

    2020-11-04 14:16:52.028 5318-8372/com.xxx D/LeakCanary: ​
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ====================================
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: HEAP ANALYSIS RESULT
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ====================================
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: 2 APPLICATION LEAKS
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: References underlined with "~~~" are likely causes.
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Learn more at https://squ.re/leaks.
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: 193757 bytes retained by leaking objects
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Signature: 4278cbd4494049851ebec273d84d9d93819e469
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ┬───
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │ GC Root: Global variable in native code
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ├─ dalvik.system.PathClassLoader instance
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Leaking: NO (LPRManager$LazyHolder↓ is not leaking and A ClassLoader is never leaking)
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    ↓ PathClassLoader.runtimeInternalObjects
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ├─ java.lang.Object[] array
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Leaking: NO (LPRManager$LazyHolder↓ is not leaking)
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    ↓ Object[].[1652]
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ├─ exocr.lpr.LPRManager$LazyHolder class
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Leaking: NO (a class is never leaking)
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    ↓ static LPRManager$LazyHolder.INSTANCE
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │                                   ~~~~~~~~
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ├─ exocr.lpr.LPRManager instance
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Leaking: UNKNOWN
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Retaining 379738 bytes in 4132 objects
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    mContext instance of com.xxx.module.self.carenjoyment.view.CarEnjoymentInputCarNoActivity with mDestroyed = true
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    ↓ LPRManager.mContext
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │                 ~~~~~~~~
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ╰→ com.xxx.module.self.carenjoyment.view.CarEnjoymentInputCarNoActivity instance
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     Leaking: YES (ObjectWatcher was watching this because com.xxx.module.self.carenjoyment.view.
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     CarEnjoymentInputCarNoActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     Retaining 193757 bytes in 3100 objects
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     key = e9801daf-2a1f-4f71-9269-735fde0b51d3
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     watchDurationMillis = 6712
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     retainedDurationMillis = 1708
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     mContext instance of com.xxx.module.self.carenjoyment.view.CarEnjoymentInputCarNoActivity with mDestroyed = true
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     mApplication instance of com.xxx.EhaiApplication
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     mBase instance of android.app.ContextImpl, not wrapping known Android context
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: 53850 bytes retained by leaking objects
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Signature: 93e9a0d17de54d359228d39b68810ccb2228b8c
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ┬───
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │ GC Root: Global variable in native code
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ├─ dalvik.system.PathClassLoader instance
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Leaking: NO (LPRManager$LazyHolder↓ is not leaking and A ClassLoader is never leaking)
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    ↓ PathClassLoader.runtimeInternalObjects
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ├─ java.lang.Object[] array
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Leaking: NO (LPRManager$LazyHolder↓ is not leaking)
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    ↓ Object[].[1652]
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ├─ exocr.lpr.LPRManager$LazyHolder class
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Leaking: NO (a class is never leaking)
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    ↓ static LPRManager$LazyHolder.INSTANCE
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │                                   ~~~~~~~~
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ├─ exocr.lpr.LPRManager instance
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Leaking: UNKNOWN
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Retaining 379738 bytes in 4132 objects
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    mContext instance of com.xxx.module.self.carenjoyment.view.CarEnjoymentInputCarNoActivity with mDestroyed = true
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    ↓ LPRManager.recoHandler
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │                 ~~~~~~~~~~~
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ├─ exocr.lpr.CardRecoActivity$2 instance
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Leaking: UNKNOWN
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Retaining 53882 bytes in 694 objects
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    Anonymous subclass of android.os.Handler
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    this$0 instance of exocr.lpr.CardRecoActivity with mDestroyed = true
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │    ↓ CardRecoActivity$2.this$0
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: │                         ~~~~~~
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ╰→ exocr.lpr.CardRecoActivity instance
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     Leaking: YES (ObjectWatcher was watching this because exocr.lpr.CardRecoActivity received Activity#onDestroy()
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     callback and Activity#mDestroyed is true)
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     Retaining 53850 bytes in 693 objects
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     key = 33bc1d1e-df80-4a34-b8ac-7aba952445ad
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     watchDurationMillis = 9994
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     retainedDurationMillis = 4993
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     mApplication instance of com.xxx.EhaiApplication
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ​     mBase instance of android.app.ContextImpl, not wrapping known Android context
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ====================================
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: 0 LIBRARY LEAKS
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over.
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: ====================================
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: METADATA
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Please include this in bug reports and Stack Overflow questions.
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Build.VERSION.SDK_INT: 29
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Build.MANUFACTURER: OPPO
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: LeakCanary version: 2.5
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: App process name: com.xxx
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Stats: LruCache[maxSize=3000,hits=6508,misses=91808,hitRate=6%]
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: RandomAccess[bytes=5054703,reads=91808,travel=44562833587,range=28229152,size=33999969]
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Analysis duration: 8657 ms
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Heap dump file path: /data/user/0/com.xxx/files/leakcanary/2020-11-04_14-16-40_008.hprof
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Heap dump timestamp: 1604470612004
    2020-11-04 14:16:52.029 5318-8372/com.xxx D/LeakCanary: Heap dump duration: 2849 ms
    

    Step2 - Find Leak from program

    我们看下代码,看看是怎么泄漏的。


    recognize()方法最终跳转到了SDK中的扫车牌界面。


    内部类单例
    private void startDeteted() {
            this.bCamera = hardwareSupportCheck();
            if (this.bCamera) {
                // 最终进行了页面跳转
                Intent intent = new Intent(this.mContext, CardRecoActivity.class);
                this.mContext.startActivity(intent);
            } else {
                if (this.dataCallBack != null) {
                    this.dataCallBack.onCameraDenied();
                }
                if (this.mViewEvent != null) {
                    this.mViewEvent.onCameraDenied();
                }
            }
        }
    
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
        // 省略部分代码
        CardRecoActivity.this.restartPreview();
        }
    };
    

    原来还是比较常见的Handler导致的内存泄漏啊......

    相关文章

      网友评论

          本文标题:Android内存泄漏解决流程

          本文链接:https://www.haomeiwen.com/subject/pllgvktx.html