目录

  • KOOM 功能
  • 1. Java Heap 泄漏监控
  • 1.1 OOMMonitor 介绍
  • 1.2 OOMMonitor 适用范围
  • 1.3 OOMMonitor 接入
  • 2. Native Heap 泄漏监控
  • 2.1 LeakMonitor 介绍
  • 2.2 LeakMonitor 适用范围
  • 2.3 LeakMonitor 接入
  • 3. Thread 泄漏监控
  • 3.1 ThreadLeakMonitor 介绍
  • 3.2 ThreadLeakMonitor 适用范围
  • 3.3 ThreadLeakMonitor 接入

  KOOM(Kwai OOM, Kill OOM)是快手性能优化团队在处理移动端OOM问题的过程中沉淀出的一套完整解决方案。

KOOM 功能

Koom流程:

堆外内存 监控 内存泄露监控_linux

OOM问题原因分类

  1. Java堆内存溢出
  2. 无足够连续内存空间
  3. FD(文件描述符)超出限制
  4. 线程数量超出限制
  5. 虚拟内存不足

1. Java Heap 泄漏监控

  koom-java-leak 模块用于 Java Heap 泄漏监控:它利用 Copy-on-write 机制 fork 子进程 dump Java Heap,解决了 dump 过程中 app 长时间冻结的问题

  • fork子进程去执行dumpHprofData方法
  • fork进程采用的是“Copy On Write”技术,只有在进行写入操作时,才会为子进程拷贝分配独立的内存空间,默认情况下,子进程可以和父进程共享同个内存空间,所以,当我们要执行dumpHprofData方法时,可以先fork一个子进程,它拥有父进程的内存副本,然后在子进程中执行dumpHprofData方法,而父进程则可以正常继续运行。   hprof是基于JVMTI实现的内存分析器代理,其转储文件记录了Java的内存镜像(Heap Profile),其中记录了内存堆详细的使用信息,可用于分析Java程序内存的各种性能问题。   虚拟机提供的Debug.dumpHprofData可以将hprof文件输出在指定的文件中,但是这个过程会“冻结”整个应用进程,造成数秒甚至数十秒内用户无法操作,即dump文件也是会产生STW(Stop the world),跟GC一样会让所有线程挂起。

HeapMonitor.java

public boolean isTrigger() {
    if (!started) {
      return false;
    }

    HeapStatus heapStatus = currentHeapStatus();

    if (heapStatus.isOverMaxThreshold) {
      // 已达到最大阀值,强制触发trigger,防止后续出现大内存分配导致OOM进程Crash,无法触发trigger
      KLog.i(TAG, "heap used is over max ratio, force trigger and over times reset to 0");
      currentTimes = 0;
      return true;
    }

    if (heapStatus.isOverThreshold) {
      KLog.i(TAG, "heap status used:" + heapStatus.used / KConstants.Bytes.MB
              + ", max:" + heapStatus.max / KConstants.Bytes.MB
              + ", last over times:" + currentTimes);
      if (heapThreshold.ascending()) {
      	// 第一次进来 或者 当前内存占用率跟上次高 或者 当前内存占用率超过了最大的阈值(95%)
        if (lastHeapStatus == null || heapStatus.used >= lastHeapStatus.used || heapStatus.isOverMaxThreshold) {
          currentTimes++;
        } else {
          KLog.i(TAG, "heap status used is not ascending, and over times reset to 0");
          currentTimes = 0;
        }
      } else {
        currentTimes++;
      }
    } else {
      currentTimes = 0;
    }

    lastHeapStatus = heapStatus;
    return currentTimes >= heapThreshold.overTimes();
  }

Koom中设置了五种检测器 ① HeapOOMTracker ② ThreadOOMTracker ③ FdOOMTracker ④ PhysicalMemoryOOMTracker ⑤ FastHugeMemoryOOMTracker前三种称为长期高内存检测器,检测机制是当计算出内存占用率之后,我们看下面的一个判断条件,如果内存占用率超过我们设定的一个阈值(例如0.8),而且当前内存占用率跟上次比较超过了千分之5,那么mOverThresholdCount变量就会自增1。

Heap监测机制是第一次进来 或者 当前内存占用率跟上次高 或者 当前内存占用率超过了最大的阈值(95%),三个条件只要满足一个,那么mOverThresholdCount变量就会自增1

  因为检测是一个循环的过程,所以当第一次进来的时候,一定会自增1,而且会将本次的内存占用率赋值给mLastHeapRatio,当下次进来的时候,如果内存占用率较上次降低了,那么就会重置。   如此往复,当mOverThresholdCount超出我们设置的阈值(例如3次),我们就认定系统发生了内存泄漏,这个时候就需要告警,并dump内存快照分析问题。 即 1)内存占用超过80%,2)并且检测到了3次,两个条件同时满足就会进行dump。第四种代码中返回false没有进行处理。第五种称为高危内存与快速增长检测器,检测机制是内存占用超过90%或者本次检测与上次检测内存占用超过350M,满足两个条件之一就会进行dump。

1.1 OOMMonitor 介绍

用于监控应用的 Java 内存泄漏问题,它的核心原理:

  • 周期性查询Java堆内存、线程数、文件描述符数等资源占用情况,当连续多次超过设定阈值或突发性连续快速突破高阈值时,触发镜像采集
  • 镜像采集采用虚拟机supend->fork虚拟机进程->虚拟机resume->dump内存镜像的策略,将传统Dump冻结进程20s的时间缩减至20ms以内
  • 基于shark执行镜像解析,并针对shark做了一系列调整用于提升性能,在手机设备测即可执行离线内存泄露判定与引用链查找,生成分析报告

1.2 OOMMonitor 适用范围

  • Android L 及以上(API level >= 21)
  • 支持 armeabi-v7a arm64-v8a x86 x86-64

1.3 OOMMonitor 接入

OOMMonitor 接入

2. Native Heap 泄漏监控

  koom-native-leak 模块用于 Native Heap 泄漏监控:它利用 Tracing garbage collection 机制分析整个 Native Heap,直接输出泄漏内存信息「大小、分配堆栈等』;极大的降低了业务同学分析、解决内存泄漏的成本。

思路总结:

  • hook malloc/free等内存分配器方法,用于记录Native内存分配元数据[大小、堆栈、地址等]–使用了爱奇艺的xhook库
  • 周期性的使用mark-and-sweep分析整个进程Native Heap, 获取不可达的内存块信息[地址、大小]–使用到google的libmemunreachable库
  • 利用不可达的内存块的地址、大小等从我们记录的元数据中获取其分配堆栈,产出泄漏数据[不可达内存地址、大小、分配堆栈等]

2.1 LeakMonitor 介绍

用于监控应用的 Native 内存泄漏问题,它的核心原理如下:

  • hook malloc/free 等内存分配器方法,用于记录 Native 内存分配元数据「大小、堆栈、地址等」
  • 周期性的使用 mark-and-sweep 分析整个进程 Native Heap,获取不可达的内存块信息「地址、大小」
  • 利用不可达的内存块的地址、大小等从我们记录的元数据中获取其分配堆栈,产出泄漏数据「不可达内存块地址、大小、分配堆栈等」

2.2 LeakMonitor 适用范围

  • Android N 及以上(API level >= 24)
  • 仅支持 arm64-v8a

2.3 LeakMonitor 接入

LeakMonitor 接入

3. Thread 泄漏监控

  koom-thread-leak 模块用于 Thread 泄漏监控:它会 hook 线程的生命周期函数,周期性的上报泄漏线程信息。

3.1 ThreadLeakMonitor 介绍

用于监控应用的线程泄漏问题,它的核心原理:

  • hook pthread_create/pthread_exit 等线程方法,用于记录线程的生命周期和创建堆栈,名称等信息
  • 当发现一个joinable的线程在没有detach或者join的情况下,执行了pthread_exit,则记录下泄露线程信息
  • 当线程泄露时间到达配置设置的延迟期限的时候,上报线程泄露信息

3.2 ThreadLeakMonitor 适用范围

  • Android N 及以上(API level >= 24)
  • 仅支持 arm64-v8a

3.3 ThreadLeakMonitor 接入

ThreadLeakMonitor 接入