0
点赞
收藏
分享

微信扫一扫

内存泄漏之KOOM



文章目录

  • 资料
  • Hprof文件分析
  • Android中的Hprof
  • 管理应用内存
  • 继承ComponentCallbacks2接口,并onTrimMemory中回调
  • 查看您应该使用多少内存
  • 怎么引发内存泄漏
  • Android中的泄漏对象
  • 泄漏的形式
  • Profiler的使用
  • 内存性能分析器Profiler概览
  • Heap分区
  • Instance View
  • Depth
  • Native Size
  • Shallow Size
  • Retained Size
  • Reference
  • KOOM
  • KOOM的整体依赖图
  • 无主动出发GC不卡顿
  • 启动服务之前创建的Intent传参
  • HeapAnalysisService
  • HeapGraph
  • ForkJvmHeapDumper
  • JavaHeap(内存使用信息)
  • ProcStatus
  • MemInfo
  • hprofStringCache
  • LeakCanary
  • LeakCanary#install
  • ActivityRefWatcher
  • RefWatcher
  • RefWatcher#ensureGone
  • HeapDumper#dumpHeap
  • HeapAnalyzerService
  • HeapAnalyzer#checkForleak
  • AbstractAnalysisResultService
  • 09.DisplayLeakService


资料

Hprof AgentKOOMHprof
android中hprof文件分析
快照技术(COW与ROW技术)
Android内存泄漏简介关于Hprof的方方面面内存性能分析器中的泄漏检测分析应用性能使用内存性能分析器查看应用的内存使用情况管理应用内存Android开发之——Profiler-内存分析
/proc/self/status讲解
Linux EUID,SUID,RUID简单理解
Linux:/proc/meminfo参数详细解释
读取Android设备的内存数据- USS,PSS,RSS
DebugLinux内存那些事 – VSS、RSS、PSS、USS
Linux 进程内存使用统计VSS RSS PSS USSS
Andorid9.0和10.0下的的libart.so
深入理解 Android 之 LeakCanary 源码解析

Hprof文件分析

Hprof最初是由J2SE支持的一种二进制堆转储格式,hprof文件保存了当前java堆上所有的内存使用信息,能够完整的反映虚拟机当前的内存状态。

内存泄漏之KOOM_android studio


Hprof文件由FixedHead和一系列的Record组成,Record包含字符串信息、类信息、栈信息、GcRoot信息、对象信息。每个Record都是由1个字节的Tag、4个字节的Time、4个字节的Length和Body组成,Tag表示该Record的类型,Body部分为该Record的内容,长度为Length。

Android中的Hprof

内存泄漏之KOOM_android-studio_02

管理应用内存

继承ComponentCallbacks2接口,并onTrimMemory中回调

您的应用可以在处于前台或后台时监听与内存相关的事件,然后释放对象以响应指示系统需要回收内存的应用生命周期事件或系统事件。

import android.content.ComponentCallbacks2;
    public class MainActivity extends AppCompatActivity
        implements ComponentCallbacks2 {
        /**
         * Release memory when the UI becomes hidden or when system resources become low.
         * @param level the memory-related event that was raised.
         */
        public void onTrimMemory(int level) {
            // Determine which lifecycle or system event was raised.
            switch (level) {
                case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
                    /*
                       Release any UI objects that currently hold memory.
                       The user interface has moved to the background.
                    */
                    break;
                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
                    /*
                       Release any memory that your app doesn't need to run.
                       The device is running low on memory while the app is running.
                       The event raised indicates the severity of the memory-related event.
                       If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
                       begin killing background processes.
                    */
                    break;
                case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
                case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
                    /*
                       Release as much memory as the process can.
                       The app is on the LRU list and the system is running low on memory.
                       The event raised indicates where the app sits within the LRU list.
                       If the event is TRIM_MEMORY_COMPLETE, the process will be one of
                       the first to be terminated.
                    */
                    break;
                default:
                    /*
                      Release any non-critical data structures.
                      The app received an unrecognized memory level value
                      from the system. Treat this as a generic low-memory message.
                    */
                    break;
            }
        }
    }

查看您应该使用多少内存

private String getAvailableMemory() {
        ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
        activityManager.getMemoryInfo(memoryInfo);
        StringBuilder sb = new StringBuilder();
        sb.append("availMem = ").append(memoryInfo.availMem).append("\n")
            .append("totalMem = ").append(memoryInfo.totalMem).append("\n")
            .append("threshold = ").append(memoryInfo.threshold).append("\n")
            .append("lowMemory = ").append(memoryInfo.lowMemory).append("\n");
        return sb.toString();
    }

怎么引发内存泄漏

Android中的泄漏对象

判断一个对象是否泄漏首先要判断该对象是否不再使用,想要判断这一点则需要对象有明显的生命周期,在Android中有以下对象可以判断是否泄漏:

  • 1、Activity: 通过Activity的mDestroyed属性来判断该Activity是否已经销毁,如果已经销毁且未被回收则认为是泄漏
    Fragment: 通过Fragment的mFragmentManager是否为空来判断该Fragment是否处于无用状态,如果mFragmentManager为空且未被回收则认为是泄漏。
  • 2、View: 通过unwrapper mContext获得Activity,如果存在Activity,则判断该Activity是否泄漏。
    Editor: Editor指的是android.widget包下的Editor,是用于TextView处理editable text的辅助类,通过mTextView是否为空来判断Editor是否处于无用状态,如果mTextView为空且未被回收则认为是泄漏。
  • 3、ContextWrapper: 通过unwrapper ContextWrapper获得Activity,如果存在Activity,则判断该Activity是否泄漏。
  • 4、Dialog: 通过mDecor是否为空判断该Dialog是否处于无用状态,如果mDecor为空且未被回收则认为是泄漏。
  • 5、MessageQueue: 通过mQuitting或者mQuiting(应该是历史原因,前期拼写错误为mQuiting,后来改正)来判断MessageQueue是否已经退出,如果已经退出且未被回收则认为是泄漏。
  • ViewRootImpl: 通过ViewRootImpl的mView是否为空来判断该ViewRootImpl是否处于无用状态,如果mView为空且未被回收则认为是泄漏。
  • 6、Window: 通过mDestroyed来判断该Window是否处于无用状态,如果mDestroyed为true且未被回收则认为是泄漏。
    Toast: 拿到mTN,通过mTN的mView是否为空来判断当前Toast是否已经hide,如果已经hide且未被回收则认为是泄漏。

泄漏的形式

泄漏的形式
泄漏的本质就是无用对象被持有导致无法回收,具体的形式有如下几种:

  • 1、非静态内部类、匿名内部类持有外部类对象引用: 一般为用于回调的Listener,该Listener被别的地方持有,间接导致外部类对象被泄漏。
  • 2、Handler: 在Activity中定义Handler对象的时候,Handler持有Activity同时Message持有Handler,而Message被MessageQueue持有,最终导致Activity泄漏。
  • 3、资源对象未关闭: 数据库连接、Cursor、IO流等使用完后未close。
  • 4、属性动画: 使用ValueAnimator和ObjectAnimator的时候,未及时关闭动画导致泄漏。Animator内部向AnimationHandler注册listener,AnimationHandler是一个单例,如果不及时cancel,会导致Animator泄漏,间接导致Activity/Fragment/View泄漏(比如Animator的updateListener一般都以匿名内部类实现)
  • 5、逻辑问题: 注册监听器之后未及时解注册,比如使用EventBus的时候没有在合适的时候进行解注册
  1. Handler引起的内存泄露
  2. 单例引起的内存泄露
  3. 外部类静态对象持有内部类对象

Profiler的使用

内存性能分析器Profiler概览

内存泄漏之KOOM_android-studio_03

  • 捕获堆转储(Capture heap dump):查看应用程序中在特定时间点使用内存的对象
  • 记录Native分配(Record native allocations):查看每个C/C++对象在一段时间内是如何分配的
  • 记录java/kotlin分配(Record java/kotlin allocations):查看在一段时间内如何分配每个java/kotlin对象

内存泄漏之KOOM_Android_04

Heap分区

内存泄漏之KOOM_android_05

    1. app heap:当前APP从堆中分配的内存
    1. image heap:系统启动映像,包含启动期间预加载的类。此处的分配保证绝不会移动或消失
    1. zygote heap:zygote是所有APP进程的母进程,linux的进程使用COW技术,所有的APP共享zygote的内存空间,因此堆的话也继承了,并且zygote的这些空间不允许写入,为了加快java的启动和运行速度,zygote在启动时预加载了许多资源和代码,这样可以提高APP的运行效率。
      源码

enum HprofHeapId {
 HPROF_HEAP_DEFAULT= 0,
 HPROF_HEAP_ZYGOTE= 'Z',
 HPROF_HEAP_APP= 'A',
 HPROF_HEAP_IMAGE= 'I',
};
 if (space->IsZygoteSpace()) {
     heap_type= HPROF_HEAP_ZYGOTE;
     VisitRoot(obj, RootInfo(kRootVMInternal));
   } else if (space->IsImageSpace() && heap->ObjectIsInBootImageSpace(obj)) {
     // Only countobjects in the boot image as HPROF_HEAP_IMAGE, this leaves app image objects as
     // HPROF_HEAP_APP.b/35762934
     heap_type= HPROF_HEAP_IMAGE;
     VisitRoot(obj, RootInfo(kRootVMInternal));
   }
 } else {
   const auto* los = heap->GetLargeObjectsSpace();
   if (los->Contains(obj) && los->IsZygoteLargeObject(Thread::Current(), obj)) {
     heap_type= HPROF_HEAP_ZYGOTE;
     VisitRoot(obj, RootInfo(kRootVMInternal));
   }

Instance View

内存泄漏之KOOM_android studio_06

Depth

depth是从gc roots到选中的当前对象的引用链最短长度。在java垃圾回收机制中,被gc roots引用到的对象不会被回收。如下图:在gc root 引用树之外的对象会被回收。

内存泄漏之KOOM_java_07


Android中gc root主要分为下面几类:

内存泄漏之KOOM_android studio_08

Native Size

Native Size对象的内存大小,这个值只有在Android 7.0以及更高的版本可见。

Shallow Size

对象本身占用的内存,不包括它引用的其它实例。
Shallow Size = [类定义] + 父类fields所占空间 + 自身fields所占空间 + [alignment]

Retained Size

Retained Size是指,当实例A被回收时,可以同时被回收的实例的Shallow Size之和,所以进行内存分析时,应该重点关注Retained Size较大的实例;或者可以通过Retained Size判断出某A实例内部使用的实例是否被其它实例引用。

内存泄漏之KOOM_android studio_09

Reference

Reference里面主要是类的引用关系,可以通过引用关系,一层一层的查看类是如何泄漏的

KOOM

KOOM的整体依赖图

内存泄漏之KOOM_java_10

无主动出发GC不卡顿

内存泄漏之KOOM_android studio_11

启动服务之前创建的Intent传参

REASON=reanalysis
THREAD=714
JAVA_MAX_MEM=192.0
USAGE_TIME=5
DEVICE_AVA_MEM=3476.6563
CURRENT_PAGE=javaleak.JavaLeakTestActivity
RESULT_RECEIVER=com.kwai.koom.javaoom.monitor.analysis.AnalysisReceiver@f003f7
FD=32
PSS=62.92871mb
RSS=91.58594mb
SDK=23
VSS=1643.3594mb
TIME=2022-06-19_21-09-10_345
MODEL=MuMu
JAVA_USED_MEM=12.838821
HPROF_FILE=/storage/emulated/0/Android/data/com.kwai.koom.demo/files/performance/oom/memory/hprof-aly/1.0.0_2022-06-19_21-00-03_901.hprof
DEVICE_MAX_MEM=3953.0703
MANUFACTURE=Netease
JSON_FILE=/storage/emulated/0/Android/data/com.kwai.koom.demo/files/performance/oom/memory/hprof-aly/1.0.0_2022-06-19_21-00-03_901.json
ROOT_PATH=/storage/emulated/0/Android/data/com.kwai.koom.demo/files/performance/oom

HeapAnalysisService

/**
 * Copyright 2021 Kwai, Inc. All rights reserved.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * <p>
 * Heap report file json format.
 *
 * @author Rui Li <lirui05@kuaishou.com>
 */

package com.kwai.koom.javaoom.monitor.analysis

import android.annotation.SuppressLint
import android.app.IntentService
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Debug
import android.os.ResultReceiver
import com.google.gson.Gson
import com.kwai.koom.base.MonitorLog
import com.kwai.koom.javaoom.monitor.OOMFileManager
import com.kwai.koom.javaoom.monitor.OOMFileManager.createDumpFile
import com.kwai.koom.javaoom.monitor.OOMFileManager.fdDumpDir
import com.kwai.koom.javaoom.monitor.OOMFileManager.threadDumpDir
import com.kwai.koom.javaoom.monitor.tracker.model.SystemInfo.javaHeap
import com.kwai.koom.javaoom.monitor.tracker.model.SystemInfo.memInfo
import com.kwai.koom.javaoom.monitor.tracker.model.SystemInfo.procStatus
import com.kwai.koom.javaoom.monitor.utils.SizeUnit.BYTE
import com.kwai.koom.javaoom.monitor.utils.SizeUnit.KB
import kshark.*
import kshark.HeapAnalyzer.FindLeakInput
import kshark.HprofHeapGraph.Companion.openHeapGraph
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlin.system.measureTimeMillis

class HeapAnalysisService : IntentService("HeapAnalysisService") {
    companion object {
        private const val TAG = "OOMMonitor_HeapAnalysisService"

        private const val OOM_ANALYSIS_TAG = "OOMMonitor"
        private const val OOM_ANALYSIS_EXCEPTION_TAG = "OOMMonitor_Exception"

        //Activity->ContextThemeWrapper->ContextWrapper->Context->Object
        private const val ACTIVITY_CLASS_NAME = "android.app.Activity"

        //Bitmap->Object
        //Exception: Some OPPO devices
        const val BITMAP_CLASS_NAME = "android.graphics.Bitmap"

        //Fragment->Object
        private const val NATIVE_FRAGMENT_CLASS_NAME = "android.app.Fragment"

        // native android Fragment, deprecated as of API 28.
        private const val SUPPORT_FRAGMENT_CLASS_NAME = "android.support.v4.app.Fragment"

        // pre-androidx, support library version of the Fragment implementation.
        private const val ANDROIDX_FRAGMENT_CLASS_NAME = "androidx.fragment.app.Fragment"

        // androidx version of the Fragment implementation
        //Window->Object
        private const val WINDOW_CLASS_NAME = "android.view.Window"

        //NativeAllocationRegistry
        private const val NATIVE_ALLOCATION_CLASS_NAME = "libcore.util.NativeAllocationRegistry"
        private const val NATIVE_ALLOCATION_CLEANER_THUNK_CLASS_NAME =
            "libcore.util.NativeAllocationRegistry\$CleanerThunk"

        private const val FINISHED_FIELD_NAME = "mFinished"
        private const val DESTROYED_FIELD_NAME = "mDestroyed"

        private const val FRAGMENT_MANAGER_FIELD_NAME = "mFragmentManager"
        private const val FRAGMENT_MCALLED_FIELD_NAME = "mCalled"

        private const val DEFAULT_BIG_PRIMITIVE_ARRAY = 256 * 1024
        private const val DEFAULT_BIG_BITMAP = 768 * 1366 + 1
        private const val DEFAULT_BIG_OBJECT_ARRAY = 256 * 1024
        private const val SAME_CLASS_LEAK_OBJECT_PATH_THRESHOLD = 45 // threshold 阈值

        annotation class Info {
            companion object {
                internal const val HPROF_FILE = "HPROF_FILE"
                internal const val JSON_FILE = "JSON_FILE"
                internal const val ROOT_PATH = "ROOT_PATH"
                internal const val RESULT_RECEIVER = "RESULT_RECEIVER"

                internal const val JAVA_MAX_MEM = "JAVA_MAX_MEM"
                internal const val JAVA_USED_MEM = "JAVA_USED_MEM"
                internal const val DEVICE_MAX_MEM = "DEVICE_MAX_MEM"
                internal const val DEVICE_AVA_MEM = "DEVICE_AVA_MEM"
                internal const val VSS = "VSS"
                internal const val PSS = "PSS"
                internal const val RSS = "RSS"
                internal const val FD = "FD"
                internal const val THREAD = "THREAD"
                internal const val SDK = "SDK"
                internal const val MANUFACTURE = "MANUFACTURE"
                internal const val MODEL = "MODEL"
                internal const val TIME = "TIME"
                internal const val REASON = "REASON"
                internal const val USAGE_TIME = "USAGE_TIME"
                internal const val CURRENT_PAGE = "CURRENT_PAGE"
            }
        }

        fun startAnalysisService(
            context: Context, hprofFile: String?, jsonFile: String?,
            extraData: AnalysisExtraData, resultCallBack: AnalysisReceiver.ResultCallBack?
        ) {
            MonitorLog.i(TAG, "startAnalysisService")

            val analysisReceiver = AnalysisReceiver().apply {
                setResultCallBack(resultCallBack)
            }

            val intent = Intent(context, HeapAnalysisService::class.java).apply {
                putExtra(Info.HPROF_FILE, hprofFile)
                putExtra(Info.JSON_FILE, jsonFile)
                putExtra(Info.ROOT_PATH, OOMFileManager.rootDir.absolutePath)
                putExtra(Info.RESULT_RECEIVER, analysisReceiver)

                putExtra(Info.JAVA_MAX_MEM, BYTE.toMB(javaHeap.max).toString())
                putExtra(Info.JAVA_USED_MEM, BYTE.toMB(javaHeap.total - javaHeap.free).toString())
                putExtra(Info.DEVICE_MAX_MEM, KB.toMB(memInfo.totalInKb).toString())
                putExtra(Info.DEVICE_AVA_MEM, KB.toMB(memInfo.availableInKb).toString())

                putExtra(Info.FD, (File("/proc/self/fd").listFiles()?.size ?: 0).toString())

                val pss = Debug.getPss() // 实际使用的物理内存
                MonitorLog.i(TAG, "startAnalysisService get Pss:${pss}")
                putExtra(Info.PSS, KB.toMB(pss).toString() + "mb")

                putExtra(Info.VSS, KB.toMB(procStatus.vssInKb).toString() + "mb") // 虚拟耗用内存(包含共享库占用的全部内存,以及分配但未使用内存)
                putExtra(Info.RSS, KB.toMB(procStatus.rssInKb).toString() + "mb") // 即使用的物理内存
                putExtra(Info.THREAD, procStatus.thread.toString()) //
                putExtra(Info.MANUFACTURE, Build.MANUFACTURER.toString())
                putExtra(Info.SDK, Build.VERSION.SDK_INT.toString())
                putExtra(Info.MODEL, Build.MODEL.toString())
                putExtra(Info.TIME, SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS", Locale.CHINESE).format(Date()))

                if (extraData.reason != null) {
                    putExtra(Info.REASON, extraData.reason)
                }
                if (extraData.currentPage != null) {
                    putExtra(Info.CURRENT_PAGE, extraData.currentPage)
                }
                if (extraData.usageSeconds != null) {
                    putExtra(Info.USAGE_TIME, extraData.usageSeconds)
                }
            }

            val sb = StringBuilder();
            for (key in intent.extras?.keySet().orEmpty()) {
                sb.append(key).append("=").append(intent.extras?.get(key)).appendln();
            }
            MonitorLog.i(TAG, sb.toString())

            context.startService(intent)
        }
    }

    private lateinit var mHeapGraph: HeapGraph

    private val mLeakModel = HeapReport()
    private val mLeakingObjectIds = mutableSetOf<Long>()
    private val mLeakReasonTable = mutableMapOf<Long, String>()

    override fun onHandleIntent(intent: Intent?) {
        MonitorLog.i(TAG, "onHandleIntent")
        val resultReceiver = intent?.getParcelableExtra<ResultReceiver>(Info.RESULT_RECEIVER)
        val hprofFile = intent?.getStringExtra(Info.HPROF_FILE)
        val jsonFile = intent?.getStringExtra(Info.JSON_FILE)
        val rootPath = intent?.getStringExtra(Info.ROOT_PATH)

        OOMFileManager.init(rootPath)

        kotlin.runCatching {
            // 解析堆转储文件
            buildIndex(hprofFile)
        }.onFailure {
            it.printStackTrace()
            MonitorLog.e(OOM_ANALYSIS_EXCEPTION_TAG, "build index exception " + it.message, true)
            resultReceiver?.send(AnalysisReceiver.RESULT_CODE_FAIL, null)
            return
        }

        // 只是创建一个文件
        buildJson(intent)

        kotlin.runCatching {
            // 筛选出内存泄漏的对象
            filterLeakingObjects()
        }.onFailure {
            MonitorLog.i(OOM_ANALYSIS_EXCEPTION_TAG, "find leak objects exception " + it.message, true)
            resultReceiver?.send(AnalysisReceiver.RESULT_CODE_FAIL, null)
            return
        }

        kotlin.runCatching {
            // 从GcRoot到泄漏点的路径
            findPathsToGcRoot()
        }.onFailure {
            it.printStackTrace()
            MonitorLog.i(OOM_ANALYSIS_EXCEPTION_TAG, "find gc path exception " + it.message, true)
            resultReceiver?.send(AnalysisReceiver.RESULT_CODE_FAIL, null)
            return
        }

        fillJsonFile(jsonFile)

        resultReceiver?.send(AnalysisReceiver.RESULT_CODE_OK, null)

        //exitProcess(0)
    }

    @SuppressLint("SdCardPath")
    private fun buildIndex(hprofFile: String?) {
        if (hprofFile.isNullOrEmpty()) return

        MonitorLog.i(TAG, "start analyze")

        SharkLog.logger = object : SharkLog.Logger {
            override fun d(message: String) {
                println(message)
            }

            override fun d(
                throwable: Throwable,
                message: String
            ) {
                println(message)
                throwable.printStackTrace()
            }
        }

        measureTimeMillis {
            mHeapGraph = File(hprofFile).openHeapGraph(
                null,
                setOf(
                    HprofRecordTag.ROOT_JNI_GLOBAL,
                    HprofRecordTag.ROOT_JNI_LOCAL,
                    HprofRecordTag.ROOT_NATIVE_STACK,
                    HprofRecordTag.ROOT_STICKY_CLASS,
                    HprofRecordTag.ROOT_THREAD_BLOCK,
                    HprofRecordTag.ROOT_THREAD_OBJECT
                )
            )
        }.also {
            try {
                MonitorLog.i(TAG, "build index cost time: 开始")
//                val toJson = Gson().toJson(mHeapGraph)
//                val toJson = mHeapGraph.toString()
                MonitorLog.i(TAG, "build index cost time: 生成JSON")
//                val file = File("/sdcard/Android/data/com.kwai.koom.demo/cache/heapgraph.txt")
//                MonitorLog.i(TAG, "build index cost time: 写入本地")
//                file.writeText(toJson)
                MonitorLog.i(TAG, "build index cost time: 本地本地完成")
            } catch (e: Exception) {
                e.printStackTrace()
                val file = File("/sdcard/Android/data/com.kwai.koom.demo/cache/heapgraph.txt")
                file.writeText(e.message ?: "error")
            }
            MonitorLog.i(TAG, "build index cost time: $it")
        }
    }

    private fun buildJson(intent: Intent?) {
        mLeakModel.runningInfo = HeapReport.RunningInfo().apply {
            jvmMax = intent?.getStringExtra(Info.JAVA_MAX_MEM)
            jvmUsed = intent?.getStringExtra(Info.JAVA_USED_MEM)
            threadCount = intent?.getStringExtra(Info.THREAD)
            fdCount = intent?.getStringExtra(Info.FD)

            vss = intent?.getStringExtra(Info.VSS)
            pss = intent?.getStringExtra(Info.PSS)
            rss = intent?.getStringExtra(Info.RSS)

            sdkInt = intent?.getStringExtra(Info.SDK)
            manufacture = intent?.getStringExtra(Info.MANUFACTURE)
            buildModel = intent?.getStringExtra(Info.MODEL)

            usageSeconds = intent?.getStringExtra(Info.USAGE_TIME)
            currentPage = intent?.getStringExtra(Info.CURRENT_PAGE)
            nowTime = intent?.getStringExtra(Info.TIME)

            deviceMemTotal = intent?.getStringExtra(Info.DEVICE_MAX_MEM)
            deviceMemAvaliable = intent?.getStringExtra(Info.DEVICE_AVA_MEM)

            dumpReason = intent?.getStringExtra(Info.REASON)

            MonitorLog.i(
                TAG, "handle Intent, fdCount:${fdCount} pss:${pss} rss:${rss} vss:${vss} " +
                    "threadCount:${threadCount}"
            )

            fdList = createDumpFile(fdDumpDir).takeIf { it.exists() }?.readLines()
            threadList = createDumpFile(threadDumpDir).takeIf { it.exists() }?.readLines()

//            createDumpFile(fdDumpDir).delete()
//            createDumpFile(threadDumpDir).delete()
        }
    }

    /**
     * 遍历镜像所有class查找
     *
     * 计算gc path:
     * 1.已经destroyed和finished的activity
     * 2.已经fragment manager为空的fragment
     * 3.已经destroyed的window
     * 4.超过阈值大小的bitmap
     * 5.超过阈值大小的基本类型数组
     * 6.超过阈值大小的对象个数的任意class
     *
     *
     * 记录关键类:
     * 对象数量
     * 1.基本类型数组
     * 2.Bitmap
     * 3.NativeAllocationRegistry
     * 4.超过阈值大小的对象的任意class
     *
     *
     * 记录大对象:
     * 对象大小
     * 1.Bitmap
     * 2.基本类型数组
     */
    private fun filterLeakingObjects() {
        val startTime = System.currentTimeMillis()
        MonitorLog.i(TAG, "filterLeakingObjects " + Thread.currentThread())

        // 各种内存泄漏的点
        val activityHeapClass: HeapObject.HeapClass? = mHeapGraph.findClassByName(ACTIVITY_CLASS_NAME)
        MonitorLog.i(TAG, "filterLeakingObjects activityHeapClass = $activityHeapClass")

        val fragmentHeapClass: HeapObject.HeapClass? = mHeapGraph.findClassByName(ANDROIDX_FRAGMENT_CLASS_NAME)
            ?: mHeapGraph.findClassByName(NATIVE_FRAGMENT_CLASS_NAME)
            ?: mHeapGraph.findClassByName(SUPPORT_FRAGMENT_CLASS_NAME)
        MonitorLog.i(TAG, "filterLeakingObjects fragmentHeapClass = $fragmentHeapClass")

        val bitmapHeapClass: HeapObject.HeapClass? = mHeapGraph.findClassByName(BITMAP_CLASS_NAME)
        MonitorLog.i(TAG, "filterLeakingObjects bitmapHeapClass = $bitmapHeapClass")

        val nativeAllocationHeapClass: HeapObject.HeapClass? = mHeapGraph.findClassByName(NATIVE_ALLOCATION_CLASS_NAME)
        MonitorLog.i(TAG, "filterLeakingObjects nativeAllocationHeapClass = $nativeAllocationHeapClass")

        val nativeAllocationThunkHeapClass: HeapObject.HeapClass? = mHeapGraph.findClassByName(NATIVE_ALLOCATION_CLEANER_THUNK_CLASS_NAME)
        MonitorLog.i(TAG, "filterLeakingObjects nativeAllocationThunkHeapClass = $nativeAllocationThunkHeapClass")

        val windowClass: HeapObject.HeapClass? = mHeapGraph.findClassByName(WINDOW_CLASS_NAME)
        MonitorLog.i(TAG, "filterLeakingObjects windowClass = $windowClass")

        //缓存classHierarchy,用于查找class的所有instance
        val classHierarchyMap = mutableMapOf<Long, Pair<Long, Long>>()
        //记录class objects数量
        val classObjectCounterMap = mutableMapOf<Long, ObjectCounter>()

        //遍历镜像的所有instance
        for (instance: HeapObject.HeapInstance in mHeapGraph.instances) {
            //HprofHeapGraph.log("HeapObject.HeapInstance ->  ${instance.instanceClassName}")
            if (instance.isPrimitiveWrapper) { // Byte,Short,Integer,Long,Float.Double,Character,Boolean
                continue
            }

            //使用HashMap缓存及遍历两边classHierarchy,这2种方式加速查找instance是否是对应类实例
            //superId1代表类的继承层次中倒数第一的id,0就是继承自object
            //superId4代表类的继承层次中倒数第四的id
            //类的继承关系,以AOSP代码为主,部分厂商入如OPPO Bitmap会做一些修改,这里先忽略
            val instanceClassId: Long = instance.instanceClassId
            //MonitorLog.i(TAG, "instance instanceClassId = $instanceClassId")
            // instance instanceClassId = 1896487800
            val (superId1: Long, superId4: Long) = if (classHierarchyMap[instanceClassId] != null) {
                classHierarchyMap[instanceClassId]!! // mutableMapOf<Long, Pair<Long, Long>>()
            } else {
                val classHierarchyList = instance.instanceClass.classHierarchy.toList()

                val first = classHierarchyList.getOrNull(classHierarchyList.size - 2)?.objectId ?: 0L
                val second = classHierarchyList.getOrNull(classHierarchyList.size - 5)?.objectId ?: 0L

                Pair(first, second).also { classHierarchyMap[instanceClassId] = it }
            }
            //MonitorLog.i(TAG, "instance superId1 = $superId1 , superId4 = $superId4")
            // instance superId1 = 1895815168 , superId4 = 0

            // 如何判断是泄露的那个类的
            //Activity
            if (activityHeapClass?.objectId == superId4) {
                val destroyField: HeapField = instance[ACTIVITY_CLASS_NAME, DESTROYED_FIELD_NAME]!!
                val finishedField: HeapField = instance[ACTIVITY_CLASS_NAME, FINISHED_FIELD_NAME]!!
                if (destroyField.value.asBoolean!! || finishedField.value.asBoolean!!) {
                    val objectCounter = updateClassObjectCounterMap(classObjectCounterMap, instanceClassId, true)
                    MonitorLog.i(
                        TAG, "activity name : " + instance.instanceClassName
                            + " mDestroyed:" + destroyField.value.asBoolean
                            + " mFinished:" + finishedField.value.asBoolean
                            + " objectId:" + (instance.objectId and 0xffffffffL)
                    )
                    if (objectCounter.leakCnt <= SAME_CLASS_LEAK_OBJECT_PATH_THRESHOLD) {
                        mLeakingObjectIds.add(instance.objectId)
                        mLeakReasonTable[instance.objectId] = "Activity Leak"
                        MonitorLog.i(OOM_ANALYSIS_TAG, "${instance.instanceClassName} objectId:${instance.objectId}")
                    }
                } else {
                    MonitorLog.i(
                        TAG, "activity name : " + instance.instanceClassName
                            + " mDestroyed:" + destroyField.value.asBoolean
                            + " mFinished:" + finishedField.value.asBoolean
                            + " objectId:" + (instance.objectId and 0xffffffffL)
                    )
                }
                continue // 找到了就不往下了
            }

            //Fragment
            if (fragmentHeapClass?.objectId == superId1) {
                val fragmentManager: HeapField? = instance[fragmentHeapClass.name, FRAGMENT_MANAGER_FIELD_NAME]
                if (fragmentManager != null && fragmentManager.value.asObject == null) {
                    val mCalledField: HeapField? = instance[fragmentHeapClass.name, FRAGMENT_MCALLED_FIELD_NAME]
                    //mCalled为true且fragment manager为空时认为fragment已经destroy
                    val isLeak: Boolean = mCalledField != null && mCalledField.value.asBoolean!!
                    val objectCounter: ObjectCounter =
                        updateClassObjectCounterMap(classObjectCounterMap, instanceClassId, isLeak)
                    MonitorLog.i(TAG, "fragment name:" + instance.instanceClassName + " isLeak:" + isLeak)
                    if (objectCounter.leakCnt <= SAME_CLASS_LEAK_OBJECT_PATH_THRESHOLD && isLeak) {
                        mLeakingObjectIds.add(instance.objectId)
                        mLeakReasonTable[instance.objectId] = "Fragment Leak"
                        MonitorLog.i(
                            OOM_ANALYSIS_TAG,
                            instance.instanceClassName + " objectId:" + instance.objectId
                        )
                    }
                }
                continue
            }

            //Bitmap
            if (bitmapHeapClass?.objectId == superId1) {
                val fieldWidth = instance[BITMAP_CLASS_NAME, "mWidth"]
                val fieldHeight = instance[BITMAP_CLASS_NAME, "mHeight"]
                val width = fieldWidth!!.value.asInt!!
                val height = fieldHeight!!.value.asInt!!
                if (width * height >= DEFAULT_BIG_BITMAP) {
                    val objectCounter = updateClassObjectCounterMap(classObjectCounterMap, instanceClassId, true)
                    MonitorLog.e(
                        TAG, "suspect leak! bitmap name: ${instance.instanceClassName}" +
                            " width: ${width} height:${height}"
                    )
                    if (objectCounter.leakCnt <= SAME_CLASS_LEAK_OBJECT_PATH_THRESHOLD) {
                        mLeakingObjectIds.add(instance.objectId)
                        mLeakReasonTable[instance.objectId] = "Bitmap Size Over Threshold, ${width}x${height}"
                        MonitorLog.i(
                            OOM_ANALYSIS_TAG,
                            instance.instanceClassName + " objectId:" + instance.objectId
                        )

                        //加入大对象泄露json
                        val leakObject: HeapReport.LeakObject = HeapReport.LeakObject().apply {
                            className = instance.instanceClassName
                            size = (width * height).toString()
                            extDetail = "$width x $height"
                            objectId = (instance.objectId and 0xffffffffL).toString()
                        }
                        mLeakModel.leakObjects.add(leakObject)
                    }
                }
                continue
            }

            //nativeallocation/NativeAllocationThunk/window
            if (nativeAllocationHeapClass?.objectId == superId1
                || nativeAllocationThunkHeapClass?.objectId == superId1
                || windowClass?.objectId == superId1
            ) {
                updateClassObjectCounterMap(classObjectCounterMap, instanceClassId, false)
            }
        }

        //关注class和对应instance数量,加入json
        for ((instanceId, objectCounter) in classObjectCounterMap) {
            val leakClass: HeapReport.ClassInfo = HeapReport.ClassInfo().apply {
                val heapClass: HeapObject.HeapClass? = mHeapGraph.findObjectById(instanceId).asClass

                className = heapClass?.name
                instanceCount = objectCounter.allCnt.toString()

                MonitorLog.i(OOM_ANALYSIS_TAG, "leakClass.className: $className leakClass.objectCount: $instanceCount")
            }

            mLeakModel.classInfos.add(leakClass)
        }

        //查找基本类型数组
        val primitiveArrayIterator: Iterator<HeapObject.HeapPrimitiveArray> = mHeapGraph.primitiveArrays.iterator()
        while (primitiveArrayIterator.hasNext()) {
            val primitiveArray = primitiveArrayIterator.next()
            val arraySize = primitiveArray.recordSize
            if (arraySize >= DEFAULT_BIG_PRIMITIVE_ARRAY) {
                val arrayName = primitiveArray.arrayClassName
                val typeName = primitiveArray.primitiveType.toString()
                MonitorLog.e(
                    OOM_ANALYSIS_TAG,
                    "uspect leak! primitive arrayName:" + arrayName
                        + " size:" + arraySize + " typeName:" + typeName
                        + ", objectId:" + (primitiveArray.objectId and 0xffffffffL)
                        + ", toString:" + primitiveArray.toString()
                )

                mLeakingObjectIds.add(primitiveArray.objectId)
                mLeakReasonTable[primitiveArray.objectId] = "Primitive Array Size Over Threshold, ${arraySize}"
                val leakObject = HeapReport.LeakObject().apply {
                    className = arrayName
                    size = arraySize.toString()
                    objectId = (primitiveArray.objectId and 0xffffffffL).toString()
                }
                mLeakModel.leakObjects.add(leakObject)
            }
        }

        //查找对象数组
        val objectArrayIterator = mHeapGraph.objectArrays.iterator()
        while (objectArrayIterator.hasNext()) {
            val objectArray = objectArrayIterator.next()
            val arraySize = objectArray.recordSize
            if (arraySize >= DEFAULT_BIG_OBJECT_ARRAY) {
                val arrayName = objectArray.arrayClassName
                MonitorLog.i(
                    OOM_ANALYSIS_TAG,
                    "object arrayName:" + arrayName + " objectId:" + objectArray.objectId
                )
                mLeakingObjectIds.add(objectArray.objectId)
                val leakObject = HeapReport.LeakObject().apply {
                    className = arrayName
                    size = arraySize.toString()
                    objectId = (objectArray.objectId and 0xffffffffL).toString()
                }
                mLeakModel.leakObjects.add(leakObject)
            }
        }

        val endTime = System.currentTimeMillis()

        mLeakModel.runningInfo?.filterInstanceTime = ((endTime - startTime).toFloat() / 1000).toString()

        MonitorLog.i(OOM_ANALYSIS_TAG, "filterLeakingObjects time:" + 1.0f * (endTime - startTime) / 1000)
    }

    // GCRootPath是什么
    private fun findPathsToGcRoot() {
        val startTime = System.currentTimeMillis()

        val heapAnalyzer = HeapAnalyzer(
            OnAnalysisProgressListener { step: OnAnalysisProgressListener.Step ->
                MonitorLog.i(TAG, "step:" + step.name + ", leaking obj size:" + mLeakingObjectIds.size)
            }
        )

        val findLeakInput: FindLeakInput =
            FindLeakInput(mHeapGraph, AndroidReferenceMatchers.appDefaults, false, mutableListOf())

        val (applicationLeaks: List<ApplicationLeak>, libraryLeaks: List<LibraryLeak>) = with(heapAnalyzer) {
            MonitorLog.i(OOM_ANALYSIS_TAG, "mLeakingObjectIds = $mLeakingObjectIds")
            findLeakInput.findLeaks(mLeakingObjectIds)
        }

        MonitorLog.i(
            OOM_ANALYSIS_TAG,
            "---------------------------Application Leak---------------------------------------"
        )
        //填充application leak
        MonitorLog.i(OOM_ANALYSIS_TAG, "ApplicationLeak size:" + applicationLeaks.size)
        for (applicationLeak in applicationLeaks) {
            MonitorLog.i(
                OOM_ANALYSIS_TAG, "shortDescription:" + applicationLeak.shortDescription
                    + ", signature:" + applicationLeak.signature
                    + " same leak size:" + applicationLeak.leakTraces.size
            )

            val (gcRootType, referencePath, leakTraceObject) = applicationLeak.leakTraces[0]

            val gcRoot: String = gcRootType.description
            val labels: Array<String> = leakTraceObject.labels.toTypedArray()
            leakTraceObject.leakingStatusReason = mLeakReasonTable[leakTraceObject.objectId].toString()

            MonitorLog.i(
                OOM_ANALYSIS_TAG, "GC Root:" + gcRoot
                    + ", leakObjClazz:" + leakTraceObject.className
                    + ", leakObjType:" + leakTraceObject.typeName
                    + ", labels:" + labels.contentToString()
                    + ", leaking reason:" + leakTraceObject.leakingStatusReason
                    + ", leaking obj:" + (leakTraceObject.objectId and 0xffffffffL)
            )

            val leakTraceChainModel = HeapReport.GCPath()
                .apply {
                    this.instanceCount = applicationLeak.leakTraces.size
                    this.leakReason = leakTraceObject.leakingStatusReason
                    this.gcRoot = gcRoot
                    this.signature = applicationLeak.signature
                }
                .also { mLeakModel.gcPaths.add(it) }

            // 添加索引到的trace path
            for (reference in referencePath) {
                val referenceName: String = reference.referenceName
                val clazz: String = reference.originObject.className
                val referenceDisplayName: String = reference.referenceDisplayName
                val referenceGenericName: String = reference.referenceGenericName
                val referenceType: String = reference.referenceType.toString()
                val declaredClassName: String = reference.owningClassName

                MonitorLog.i(
                    OOM_ANALYSIS_TAG, "clazz:" + clazz +
                        ", referenceName:" + referenceName
                        + ", referenceDisplayName:" + referenceDisplayName
                        + ", referenceGenericName:" + referenceGenericName
                        + ", referenceType:" + referenceType
                        + ", declaredClassName:" + declaredClassName
                )

                val leakPathItem: HeapReport.GCPath.PathItem = HeapReport.GCPath.PathItem().apply {
                    this.reference = if (referenceDisplayName.startsWith("["))  //数组类型[]
                        clazz
                    else
                        "$clazz.$referenceDisplayName"
                    this.referenceType = referenceType
                    this.declaredClass = declaredClassName
                }

                leakTraceChainModel.path.add(leakPathItem)
            }

            // 添加本身trace path
            leakTraceChainModel.path.add(HeapReport.GCPath.PathItem().apply {
                reference = leakTraceObject.className
                referenceType = leakTraceObject.typeName
            })
        }


        MonitorLog.i(OOM_ANALYSIS_TAG, "=======================================================================")
        MonitorLog.i(
            OOM_ANALYSIS_TAG,
            "----------------------------Library Leak--------------------------------------"
        )
        //填充library leak
        MonitorLog.i(OOM_ANALYSIS_TAG, "LibraryLeak size:" + libraryLeaks.size)
        for (libraryLeak in libraryLeaks) {
            MonitorLog.i(
                OOM_ANALYSIS_TAG, "description:" + libraryLeak.description
                    + ", shortDescription:" + libraryLeak.shortDescription
                    + ", pattern:" + libraryLeak.pattern.toString()
            )

            val (gcRootType, referencePath, leakTraceObject) = libraryLeak.leakTraces[0]
            val gcRoot: String = gcRootType.description
            val labels: Array<String> = leakTraceObject.labels.toTypedArray()
            leakTraceObject.leakingStatusReason = mLeakReasonTable[leakTraceObject.objectId].toString()

            MonitorLog.i(
                OOM_ANALYSIS_TAG, "GC Root:" + gcRoot
                    + ", leakClazz:" + leakTraceObject.className
                    + ", labels:" + labels.contentToString()
                    + ", leaking reason:" + leakTraceObject.leakingStatusReason
            )

            val leakTraceChainModel: HeapReport.GCPath = HeapReport.GCPath().apply {
                this.instanceCount = libraryLeak.leakTraces.size
                this.leakReason = leakTraceObject.leakingStatusReason
                this.signature = libraryLeak.signature
                this.gcRoot = gcRoot
            }
            mLeakModel.gcPaths.add(leakTraceChainModel)

            // 添加索引到的trace path
            for (reference in referencePath) {
                val clazz: String = reference.originObject.className
                val referenceName: String = reference.referenceName
                val referenceDisplayName: String = reference.referenceDisplayName
                val referenceGenericName: String = reference.referenceGenericName
                val referenceType: String = reference.referenceType.toString()
                val declaredClassName: String = reference.owningClassName

                MonitorLog.i(
                    OOM_ANALYSIS_TAG, "clazz:" + clazz +
                        ", referenceName:" + referenceName
                        + ", referenceDisplayName:" + referenceDisplayName
                        + ", referenceGenericName:" + referenceGenericName
                        + ", referenceType:" + referenceType
                        + ", declaredClassName:" + declaredClassName
                )

                val leakPathItem: HeapReport.GCPath.PathItem = HeapReport.GCPath.PathItem().apply {
                    this.reference = if (referenceDisplayName.startsWith("["))
                        clazz
                    else  //数组类型[]
                        "$clazz.$referenceDisplayName"
                    this.referenceType = referenceType
                    this.declaredClass = declaredClassName
                }
                leakTraceChainModel.path.add(leakPathItem)
            }

            // 添加本身trace path
            leakTraceChainModel.path.add(HeapReport.GCPath.PathItem().apply {
                reference = leakTraceObject.className
                referenceType = leakTraceObject.typeName
            })
            break
        }
        MonitorLog.i(
            OOM_ANALYSIS_TAG,
            "======================================================================="
        )

        val endTime = System.currentTimeMillis()

        mLeakModel.runningInfo!!.findGCPathTime = ((endTime - startTime).toFloat() / 1000).toString()

        MonitorLog.i(
            OOM_ANALYSIS_TAG, "findPathsToGcRoot cost time: "
                + (endTime - startTime).toFloat() / 1000
        )
    }

    private fun fillJsonFile(jsonFile: String?) {
        val json = Gson().toJson(mLeakModel)

        try {
            jsonFile?.let { File(it) }?.writeText(json)

            MonitorLog.i(OOM_ANALYSIS_TAG, "JSON write success: $json")
        } catch (e: IOException) {
            e.printStackTrace()
            MonitorLog.i(OOM_ANALYSIS_TAG, "JSON write exception: $json", true)
        }
    }

    private fun updateClassObjectCounterMap(
        classObCountMap: MutableMap<Long, ObjectCounter>,
        instanceClassId: Long,
        isLeak: Boolean
    ): ObjectCounter {
        val objectCounter = classObCountMap[instanceClassId] ?: ObjectCounter().also {
            classObCountMap[instanceClassId] = it
        }

        objectCounter.allCnt++

        if (isLeak) {
            objectCounter.leakCnt++
        }

        return objectCounter
    }

    class ObjectCounter {
        var allCnt = 0 // 需要自己+1
        var leakCnt = 0 // 需要自己+1
    }
}

HeapGraph

内存泄漏之KOOM_android-studio_12

ForkJvmHeapDumper

/*
 * Copyright 2020 Kwai, Inc. All rights reserved.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * <p>
 * A jvm hprof dumper which use fork and don't block main process.
 *
 * @author Rui Li <lirui05@kuaishou.com>
 */

package com.kwai.koom.javaoom.hprof;

import java.io.IOException;

import android.os.Build;
import android.os.Debug;

import com.kwai.koom.base.MonitorLog;

public class ForkJvmHeapDumper extends HeapDumper {

  private static final String TAG = "OOMMonitor_ForkJvmHeapDumper";

  public ForkJvmHeapDumper() {
    super();
    if (soLoaded) {
      init(); // 打开系统的库函数
    }
  }

  @Override
  public boolean dump(String path) {
    MonitorLog.i(TAG, "dump " + path);
    if (!soLoaded) {
      MonitorLog.e(TAG, "dump failed caused by so not loaded!");
      return false;
    }

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
        || Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
      MonitorLog.e(TAG, "dump failed caused by version not supported!");
      return false;
    }

    boolean dumpRes = false;
    try {
      MonitorLog.i(TAG, "before suspend and fork.");
      // 创建子进程
      // pid返回值
      //  1。 返回子进程id给父进程:
      //         因为一个进程的子进程可能有多个,并且没有一个函数可以获得一个进程的所有子进程
      //  2。 返回给子进程值为0:
      //         一个进程只会有一个,所以子进程松狮可以调用getid以获得当前进程id以调用getid获得父进程id
      //  3。 出现错误,返回负值:
      //         当前进程数已经达到系统规定的上限,这是errno的值被设置为EAGAIN
      //         系统内存不足,这时errno的值被设置为ENOMEM
      int pid = suspendAndFork();
      if (pid == 0) {
        // Child process
        Debug.dumpHprofData(path);
        exitProcess();
      } else if (pid > 0) {
        // Parent process
        dumpRes = resumeAndWait(pid);
        MonitorLog.i(TAG, "notify from pid " + pid);
      }
    } catch (IOException e) {
      MonitorLog.e(TAG, "dump failed caused by " + e.toString());
      e.printStackTrace();
    }
    return dumpRes;
  }

  // ---->https://www.jianshu.com/p/586300fdb1ce<----
  // int main(int argc, char *argv[]) {
  //   pid_t pid;
  //   int count = 0;
  //   //获得当前进程ID
  //   printf("Current Process Id = %d\n", getpid());
  //   if ((pid == fork()) < 0) {
  //     print("异常退出");
  //   } else if(pid == 0) {
  //     count++;
  //     printf("进程子进程,当前进程curretPid=%d, 父进程parentPid=%d\n", getpid(), getppid())
  //   } else {
  //     count++;
  //     printf("当前进程 currentPid = %d, Count = %d\n", getpid(), count);
  //   }
  //   printf("当前进程 currentPid = %d, Count = %d\n", getpid(), count);
  //   return 0
  // }

  /**
   * Init before do dump.
   * // 打开系统的库函数
   */
  private native void init();

  /**
   * Suspend the whole ART, and then fork a process for dumping hprof.
   *
   * @return return value of fork
   */
  private native int suspendAndFork();

  /**
   * Resume the whole ART, and then wait child process to notify.
   *
   * @param pid pid of child process.
   */
  private native boolean resumeAndWait(int pid);

  /**
   * Exit current process.
   */
  private native void exitProcess();
}

JavaHeap(内存使用信息)

JavaHeap(max=402653184, total=27552536, free=20674832, used=6877704, rate=0.017080963)

data class JavaHeap(
        var max: Long = 0, // Runtime.getRuntim().maxMemory()
        var total: Long = 0, // Runtime.getRuntime().totalMemory()
        var free: Long = 0, // Runtime.getRuntime().freeMemory()
        var used: Long = 0, // javaHeap.total - javaHeap.free
        var rate: Float = 0f // 1.0f * javaHeap.used / javaHeap.max
    )

ProcStatus

ProcStatus(thread=731, vssInKb=18421872, rssInKb=138368)

data class ProcStatus(var thread: Int = 0, var vssInKb: Int = 0, var rssInKb: Int = 0)

flannery@zhaojiandeMacBook-Pro-2 KOOM % adb shell
HWANA:/ $ cat /proc/self/status
Name:   cat    // 应用程序或命令的名字
Umask:  0000
State:  R (running)  // 任务的状态,运行/睡眠/僵死
Tgid:   30578 // 线程组号 
Ngid:   0 
Pid:    30578 // 任务ID进程ID
PPid:   30572 // 父进程ID
TracerPid:      0 // 接受跟踪该进程信息的ID号
Uid:    2000    2000    2000    2000 (Uid euid suid fsuid)
Gid:    2000    2000    2000    2000 (Gid eguid sgid fsgid)
FDSize: 64 // 文件描述符的最大个数,file->fds
Groups: 1004 1007 1011 1015 1028 3001 3002 3003 3006 3009 3011 
VmPeak:    31132 kB
VmSize:    30232 kB // 任务虚拟地址空间的大小 (total_vm-reserved_vm),其中total_vm为进程的地址空间的大小,reserved_vm:进程在预留或特殊的内存间的物理页
VmLck:         0 kB // 任务已经锁住的物理内存的大小。锁住的物理内存不能交换到硬盘(locked_vm)
VmPin:         0 kB 
VmHWM:      3160 kB
VmRSS:      3140 kB // 应用程序正在使用的物理内存的大小,就是用ps命令的参数rss的值(rss)。
RssAnon:             600 kB 
RssFile:            2360 kB
RssShmem:            180 kB
VmData:     4904 kB // 程序数据段的大小(所占虚拟内存的大小),存放初始化了的数据;(total_vm-shared_vm-stack_vm)
VmStk:       136 kB // 任务在用户态的栈的大小(stack_vm)
VmExe:       432 kB // 程序所拥有的可执行虚拟内存的大小,代码段,不包括任务使用的库(end_code-start_code)
VmLib:      3156 kB // 被映像到任务的虚拟内存空间的库的大小(exec_lib)
VmPTE:        48 kB // 该进程的所有页表的大小,单位kb。Threads共享使用该信号描述符任务的个数,在POSIX多线程应用程序中,线程组中的所有线程使用同一个信号描述符。
VmPMD:        12 kB // 
VmSwap:        0 kB
Threads:        1 // 
SigQ:   0/25471 // 待处理信号的个数
SigPnd: 0000000000000000 // 屏蔽位,存储了该线程的待处理信号
ShdPnd: 0000000000000000 // 屏蔽为,存储了该线程组的待处理信号
SigBlk: 0000000080000000 // 存放被阻塞的信号
SigIgn: 0000000000000000 // 存放被忽略的信号
SigCgt: 0000000c400084f8 // 存放被俘获到的信号
CapInh: 0000000000000000 // 
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000000000c0
CapAmb: 0000000000000000
NoNewPrivs:     0
Seccomp:        0
Speculation_Store_Bypass:       thread vulnerable
Cpus_allowed:   ff
Cpus_allowed_list:      0-7
Mems_allowed:   1
Mems_allowed_list:      0
voluntary_ctxt_switches:        0
nonvoluntary_ctxt_switches:     1

MemInfo

内存泄漏之KOOM_android_13

data class MemInfo(
        var totalInKb: Int = 0,
        var freeInKb: Int = 0,
        var availableInKb: Int = 0,
        var IONHeap: Int = 0,
        var cmaTotal: Int = 0,
        var rate: Float = 0f
    )

PD1728:/ $ cat /proc/meminfo
MemTotal:        5839880 kB // 可供kernel支配的内存
MemFree:          436760 kB // 表示系统尚未使用的内存
MemAvailable:    2830796 kB // 内核使用特定的算法估算出来的,不精确
Buffers:          145848 kB // 
Cached:          2336296 kB
SwapCached:        10448 kB
Active:          1849328 kB
Inactive:        1849508 kB
Active(anon):     886964 kB
Inactive(anon):   333128 kB
Active(file):     962364 kB
Inactive(file):  1516380 kB
Unevictable:        3592 kB
Mlocked:            3592 kB
SwapTotal:       2097148 kB
SwapFree:         587260 kB
Dirty:                96 kB
Writeback:             0 kB
AnonPages:       1216292 kB
Mapped:           882444 kB
Shmem:               780 kB
Slab:             365740 kB 
SReclaimable:     133356 kB
SUnreclaim:       232384 kB
KernelStack:       85952 kB
PageTables:       112180 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     5017088 kB
Committed_AS:   153060520 kB
VmallocTotal:   258867136 kB
VmallocUsed:           0 kB // 
VmallocChunk:          0 kB
CmaTotal:         176128 kB
CmaFree:            8076 kB
PD1728:/ $

hprofStringCache

hprofStringCache first->4224708 , second->accessibility_custom_action_22
hprofStringCache first->4203524 , second->taskAffinity
hprofStringCache first->4208309 , second->Theme_textAppearanceEasyCorrectSuggestion
hprofStringCache first->4199920 , second->AndroidManifestActivity_enabled
hprofStringCache first->4205019 , second->TRANSACTION_partitionMixed
hprofStringCache first->4220338 , second->mOnPreparedListener
hprofStringCache first->4227170 , second->androidx.appcompat.view.ViewPropertyAnimatorCompatSet
hprofStringCache first->4219142 , second->m_version_
hprofStringCache first->4219707 , second->secprops
hprofStringCache first->4194621 , second->dalvik.system.CloseGuard$DefaultReporter
hprofStringCache first->4197531 , second->endTime
hprofStringCache first->4204476 , second->LINECAP_ROUND
hprofStringCache first->4199289 , second->id_aa_ets_contentTimestamp
hprofStringCache first->4219711 , second->ABASE
hprofStringCache first->4219795 , second->tomorrow
hprofStringCache first->4212952 , second->ACQUIRE_CAUSES_WAKEUP
hprofStringCache first->4214386 , second->defaultUncaughtHandler
hprofStringCache first->4210042 , second->mCurSurfaceHolder
hprofStringCache first->4195686 , second->roundingIncrement
hprofStringCache first->4217322 , second->mFpsNumFrames
hprofStringCache first->4202630 , second->android.app.Fragment
hprofStringCache first->4217865 , second->EXTRA_CONNECTION_STATE
hprofStringCache first->4217949 , second->CHUNK_THST
hprofStringCache first->4207631 , second->INDEX_CENTER_VERTICAL
hprofStringCache first->4207766 , second->CENTER_INSIDE

LeakCanary

LeakCanary#install

public static RefWatcher install(Application application,String processType, String remotePath,
      Class<? extends AbstractAnalysisResultService> listenerServiceClass,
      ExcludedRefs excludedRefs) {
    // 判断install是否在Analyzer进程里,重复执行
    if (isInAnalyzerProcess(application)) {
      return RefWatcher.DISABLED;
    }
    enableDisplayLeakActivity(application);
    // 创建监听器
    HeapDump.Listener heapDumpListener =
        new ServiceHeapDumpListener(application, processType, listenerServiceClass);
    // Refwatcher 监控引用
    RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs);
    // ActivityRefWatcher 传入 application 和 refWatcher
    ActivityRefWatcher.installOnIcsPlus(application, refWatcher);

    return refWatcher;
  }

ActivityRefWatcher

public final class ActivityRefWatcher {
  
  @Deprecated
  public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
    install(application, refWatcher);
  }

  public static void install(Application application, RefWatcher refWatcher) {
    new ActivityRefWatcher(application, refWatcher).watchActivities();
  }

  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new Application.ActivityLifecycleCallbacks() {
        @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        }

        @Override public void onActivityStarted(Activity activity) {
        }

        @Override public void onActivityResumed(Activity activity) {
        }

        @Override public void onActivityPaused(Activity activity) {
        }

        @Override public void onActivityStopped(Activity activity) {
        }

        @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        // 将Activity中的onActivityDestoryed事件注册上去
        @Override public void onActivityDestroyed(Activity activity) {
          ActivityRefWatcher.this.onActivityDestroyed(activity);
        }
      };

  // 将 activity 事件传递到了 refwatcher
  void onActivityDestroyed(Activity activity) {
    // 回调了 refWathcher中的watch方法监控activity
    refWatcher.watch(activity);
  }
}

RefWatcher

public final class RefWatcher {

  // 执行内存泄漏检测的 executor
  private final Executor watchExecutor; 
  // 用于查询是否正在调试中,调试中不会执行内存泄漏检测
  private final DebuggerControl debuggerControl; 
  // 用于在判断内存泄露之前,再给一次GC的机会 
  private final GcTrigger gcTrigger; 
  // dump 处内存泄漏的 heap
  private final HeapDumper heapDumper; 
  // 持有那些待检测以及产生内存泄露的引用的key
  private final Set<String> retainedKeys; 
  // 用于判断弱引用所持有的对象是否已被GC
  private final ReferenceQueue<Object> queue; 
  // 用于分析产生的heap文件
  private final HeapDump.Listener heapdumpListener; 
  // 排除一些系统的bug引起的内存泄漏
  private final ExcludedRefs excludedRefs; 

  public RefWatcher(Executor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger,
      HeapDumper heapDumper, HeapDump.Listener heapdumpListener, ExcludedRefs excludedRefs) {
  }

  //...

  public void watch(Object watchedReference, String referenceName) {
   
    final long watchStartNanoTime = System.nanoTime();
    // 对一个 referenc 添加唯一的一个 key
    String key = UUID.randomUUID().toString();
    retainedKeys.add(key);
    // 将 watch 传入的对象添加一个弱引用
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);
    // 在异步线程上开始分析这个弱引用
    watchExecutor.execute(new Runnable() {
      @Override public void run() {
        ensureGone(reference, watchStartNanoTime);
      }
    });
  }

RefWatcher#ensureGone

// 避免因为gc不及时带来的误判,leakcanay会进行二次确认进行保证
  void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    // 计算机从调用 watch 到 gc 所用时间
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    // 清除此时已经到 RQ 的弱引用
    // 首先调用 removeWeaklyReachableReferences
    // 把已被回收的对象的 key 从 retainedKeys 移除,剩下的 key 都是未被回收的对象
    removeWeaklyReachableReferences();
    // 如果当前的对象已经弱可达,说明不会造成内存泄漏
    if (gone(reference) || debuggerControl.isDebuggerAttached()) {
      return;
    }
    // 如果当前检测对象没有改变其可达状态,则进行手动GC
    gcTrigger.runGc();
    // 再次清除已经到 RQ的弱引用
    removeWeaklyReachableReferences();
    // 如果此时对象还没有到队列,预期被 gc 的对象会出现在队列中,没出现则此时已经可能泄漏
    if (!gone(reference)) {
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
      // dump出来heap,此时认为内存确实已经泄漏了
      File heapDumpFile = heapDumper.dumpHeap();
      if (heapDumpFile == HeapDumper.NO_DUMP) {
        // 如果不能dump出内存则abort
        return;
      }
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
      // 去分析
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
              gcDurationMs, heapDumpDurationMs));
    }
  }
  //...
}

public interface GcTrigger {
  GcTrigger DEFAULT = new GcTrigger() {
    @Override public void runGc() {
      // Code taken from AOSP FinalizationTest:
      // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
      // java/lang/ref/FinalizationTester.java
      // System.gc() does not garbage collect every time. Runtime.gc() is
      // more likely to perfom a gc.
      Runtime.getRuntime().gc();
      enqueueReferences();
      System.runFinalization();
    }

    private void enqueueReferences() {
      // Hack. We don't have a programmatic way to wait for the reference queue daemon to move
      // references to the appropriate queues.
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        throw new AssertionError();
      }
    }
  };

  void runGc();
}

HeapDumper#dumpHeap

@Override public File dumpHeap() {
    if (!leakDirectoryProvider.isLeakStorageWritable()) {
      CanaryLog.d("Could not write to leak storage to dump heap.");
      leakDirectoryProvider.requestWritePermissionNotification();
      return NO_DUMP;
    }
    File heapDumpFile = getHeapDumpFile();
    // Atomic way to check for existence & create the file if it doesn't exist.
    // Prevents several processes in the same app to attempt a heapdump at the same time.
    boolean fileCreated;
    try {
      fileCreated = heapDumpFile.createNewFile();
    } catch (IOException e) {
      cleanup();
      CanaryLog.d(e, "Could not check if heap dump file exists");
      return NO_DUMP;
    }

    // 如果没有目录存放,则抛出
    if (!fileCreated) {
      CanaryLog.d("Could not dump heap, previous analysis still is in progress.");
      // Heap analysis in progress, let's not put too much pressure on the device.
      return NO_DUMP;
    }

    FutureResult<Toast> waitingForToast = new FutureResult<>();
    showToast(waitingForToast);

    if (!waitingForToast.wait(5, SECONDS)) {
      CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
      return NO_DUMP;
    }

    Toast toast = waitingForToast.get();
    try {
      Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
      cancelToast(toast);
      return heapDumpFile;
    } catch (Exception e) {
      cleanup();
      CanaryLog.d(e, "Could not perform heap dump");
      // Abort heap dump
      return NO_DUMP;
    }
  }

heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
              gcDurationMs, heapDumpDurationMs));

public abstract class AbstractAnalysisResultService extends IntentService

HeapAnalyzerService

public final class HeapAnalyzerService extends IntentService {
  //...
  public static void runAnalysis(Context context, HeapDump heapDump,
      Class<? extends AbstractAnalysisResultService> listenerServiceClass,String processType) {
  //...
  }

  // 重点
  @Override protected void onHandleIntent(Intent intent) {
    if (intent == null) {
      CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
      return;
    }
    String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
    String processType = intent.getStringExtra(PROCESS_TYPE);
    String processName = intent.getStringExtra(PROCESS_NAME);
      HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);
      // 使用checkForLeak这个方法来分析内存使用结果
      AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
      // 结果回调
      AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result,processType,processName);
  }
}

HeapAnalyzer#checkForleak

public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
    long analysisStartNanoTime = System.nanoTime();

    if (!heapDumpFile.exists()) {
      Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
      return failure(exception, since(analysisStartNanoTime));
    }

    try {
      // 将 dump 文件解析成 snapshot 对象,haha库的用法
      HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
      HprofParser parser = new HprofParser(buffer);
      Snapshot snapshot = parser.parse();
      deduplicateGcRoots(snapshot);

      Instance leakingRef = findLeakingReference(referenceKey, snapshot);

      // False alarm, weak reference was cleared in between key check and heap dump.
      if (leakingRef == null) {
        return noLeak(since(analysisStartNanoTime));
      }
      // 找到泄漏路径
      return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
    } catch (Throwable e) {
      return failure(e, since(analysisStartNanoTime));
    }
  }

private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot,
      Instance leakingRef) {

    ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
    ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);

    // False alarm, no strong reference path to GC Roots.
    if (result.leakingNode == null) {
      return noLeak(since(analysisStartNanoTime));
    }

    LeakTrace leakTrace = buildLeakTrace(result.leakingNode);

    String className = leakingRef.getClassObj().getClassName();

    // Side effect: computes retained size.
    snapshot.computeDominators();

    Instance leakingInstance = result.leakingNode.instance;

    long retainedSize = leakingInstance.getTotalRetainedSize();

    retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);

    // 即将跳转到haha这个库去建立最短引用路径
    return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize,
        since(analysisStartNanoTime));
  }

AbstractAnalysisResultService

public abstract class AbstractAnalysisResultService extends IntentService {


  public static void sendResultToListener(Context context, String listenerServiceClassName,
      HeapDump heapDump, AnalysisResult result,String processType,String processName) {
    Class<?> listenerServiceClass;
    try {
      listenerServiceClass = Class.forName(listenerServiceClassName);
    } catch (ClassNotFoundException e) {
      throw new RuntimeException(e);
    }
    Intent intent = new Intent(context, listenerServiceClass);
    intent.putExtra(HEAP_DUMP_EXTRA, heapDump);
    intent.putExtra(RESULT_EXTRA, result);
    intent.putExtra(PROCESS_TYPE,processType);
    intent.putExtra(PROCESS_NAME,processName);
    context.startService(intent);
  }

  public AbstractAnalysisResultService() {
    super(AbstractAnalysisResultService.class.getName());
  }

  @Override protected final void onHandleIntent(Intent intent) {
    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
    AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
    String process =  intent.getStringExtra(PROCESS_TYPE);
    String processName =  intent.getStringExtra(PROCESS_NAME);

    boolean delFile = true;
    try {
      delFile = onHeapAnalyzed(heapDump, result, process, processName);
    } finally {
      //noinspection ResultOfMethodCallIgnored
      if (delFile)
        heapDump.heapDumpFile.delete();
    }
  }

  protected abstract boolean onHeapAnalyzed(HeapDump heapDump, AnalysisResult result, String processType, String processName);
}

09.DisplayLeakService

public class DisplayLeakService extends AbstractAnalysisResultService {

    @Override
    protected final boolean onHeapAnalyzed(HeapDump heapDump, AnalysisResult result, String processType, String processName) {
        return onCustomHeapAnalyzed(heapDump, result, processType, processName);
        if (PROCESS_CLIENT.equals(processType)) {
            boolean resultSaved = false;
            boolean shouldSaveResult = result.leakFound || result.failure != null;
            if (shouldSaveResult) {
                resultSaved = saveResult(heapDump, result);

                if (result.failure != null) {
                    File resultFile = new File(heapDump.heapDumpFile.getParentFile(), "analysis_fail" + ".bt");
                    saveLeakInfo(heapDump, resultFile, leakInfo);
                }
            } else {
                //无泄露结果
                File resultFile = new File(heapDump.heapDumpFile.getParentFile(), "no_leakinfo" + ".db");
                saveLeakInfo(heapDump, resultFile, leakInfo);

            }

            Intent pendingIntent = null;
            if (resultSaved) {
               // DisplayLeakActivity显示工作
                pendingIntent = DisplayLeakActivity.createIntent(this,
                        heapDump.referenceKey, "","","");
            }

            String key = heapDump.referenceKey;
            afterClientHandling(heapDump, result, pendingIntent, key);
            return false;
        } else {
            // SERCER
            heapDump = renameHeapdump(heapDump);
            afterServerHandling(heapDump);
        }
        return true;
    }

    private boolean saveResult(HeapDump heapDump, AnalysisResult result) {
        File resultFile = new File(heapDump.heapDumpFile.getParentFile(),
                heapDump.heapDumpFile.getName() + ".result");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(resultFile);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(heapDump);
            oos.writeObject(result);
            return true;
        } catch (IOException e) {
            CanaryLog.d(e, "Could not save leak analysis result to disk.");
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ignored) {
                }
            }
        }
        return false;
    }
  	// 扩展式函数
    protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo, String processType) {
    }

    protected void afterClientHandling(HeapDump heapDump, AnalysisResult result, Intent intent, String key) {
    }

    protected void afterServerHandling(HeapDump heapDump) {
    }

    protected void afterAnalyzed(PushServiceTaskParam pushTaskParam) {

    }
}

public static boolean isInServiceProcess(Context context, Class<? extends Service> serviceClass) {
    PackageManager packageManager = context.getPackageManager();
    PackageInfo packageInfo;
    try {
      packageInfo = packageManager.getPackageInfo(context.getPackageName(), GET_SERVICES);
    } catch (Exception e) {
      CanaryLog.d(e, "Could not get package info for %s", context.getPackageName());
      return false;
    }
    String mainProcess = packageInfo.applicationInfo.processName;

    ComponentName component = new ComponentName(context, serviceClass);
    ServiceInfo serviceInfo;
    try {
      serviceInfo = packageManager.getServiceInfo(component, 0);
    } catch (PackageManager.NameNotFoundException ignored) {
      // Service is disabled.
      return false;
    }
	// 注意这里
    if (serviceInfo.processName.equals(mainProcess)) {
      CanaryLog.d("Did not expect service %s to run in main process %s", serviceClass, mainProcess);
      // Technically we are in the service process, but we're not in the service dedicated process.
      return false;
    }

    // 为PID找到对应的Process
    int myPid = android.os.Process.myPid();
    ActivityManager activityManager =
        (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.RunningAppProcessInfo myProcess = null;
    List<ActivityManager.RunningAppProcessInfo> runningProcesses =
        activityManager.getRunningAppProcesses();
    if (runningProcesses != null) {
      for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {
        if (process.pid == myPid) {
          myProcess = process;
          break;
        }
      }
    }
    if (myProcess == null) {
      CanaryLog.d("Could not find running process for %d", myPid);
      return false;
    }

    return myProcess.processName.equals(serviceInfo.processName);
  }




举报

相关推荐

0 条评论