KOOM原理

本文探讨了如何解决GC卡顿,通过内存阈值监控避免频繁触发GC。此外,文章详细介绍了如何解决Dump hprof导致的应用冻结问题,通过ForkJvmHeapDumper在子进程中异步执行,减少了用户感知的卡顿。同时,针对hprof文件过大的问题,提出了实时裁剪策略以优化磁盘占用。最后,提到了Shark解析性能的优化,包括索引、数据结构和GC根剪枝等策略。

一.解决GC卡顿


为什么LeakCanary需要主动触发GC呢?LeakCanary监控泄漏利用了弱引用的特性,为Activity创建弱引用,当Activity对象变成弱可达时(没有强引用),弱引用会被加入到引用队列中,通过在Activity.onDestroy()后连续触发两次GC,并检查引用队列,可以判定Activity是否发生了泄漏。但频繁的GC会造成用户可感知的卡顿,为解决这一问题,我们设计了全新的监控模块,通过无性能损耗的内存阈值监控来触发镜像采集,具体策略如下:

具体策略如下:

  • Java堆内存突破阈值触发采集 - 90%
  • Java堆上涨速度突破阈值触发采集 - 两次检测时间间隔内增加350M直接dump
    • 如何获取堆信息通过Runtime.getRuntime()获取
      javaHeap.max = Runtime.getRuntime().maxMemory()
      javaHeap.total = Runtime.getRuntime().totalMemory()
      javaHeap.free = Runtime.getRuntime().freeMemory()
      javaHeap.used = javaHeap.total - javaHeap.free
      javaHeap.rate = 1.0f * javaHeap.used / javaHeap.max
  • 处于高位heapThreshold,并且一直处于高位降的不明显
         heapThreshold取值如下
         maxMem >= 512 - 10 -> 0.8f
         maxMem >= 256 - 10 -> 0.85f
         else -> 0.9f
  • Java堆线程数突破阈值触发采集
    • 线程阈值默认值
      private val DEFAULT_THREAD_THRESHOLD by lazy {
          if (MonitorBuildConfig.ROM == "EMUI" &&
              Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) {
              450
          } else {
              750
          }
      }
    • 当前线程数量,读取"/proc/self/status"文件获取线程信息
      File("/proc/self/status").forEachLineQuietly { line ->
          ...
          when {
              ...
              line.startsWith("Threads") -> {
                  procStatus.thread = THREADS_REGEX.matchValue(line)
              }
          }
      }
  • 文件描述符数突破阈值触发采集 - 1000
    • 当前fd数量
      private fun getFdCount(): Int {
          return File("/proc/self/fd").listFiles()?.size ?: 0
      }

二.解决Dump hprof冻结app

Dump hprof是通过API Debug.dumpHprofData实现的,这个过程会**“冻结”**整个应用进程,造成数秒甚至数十秒内用户无法操作,这也是LeakCanary无法线上部署的最主要原因,如果能将这一过程优化至用户无感知,将会给OOM治理带来很大的想象空间。

面对这样一个问题,我们将其拆解,自然而然产生2个疑问:
1.为什么dumpHprofData会冻结app,虚拟机的实现原理是什么?
2.这个过程能异步吗?

我们来看dumpHprofData的虚拟机内部实现
art/runtime/hprof/hprof.cc

// If "direct_to_ddms" is true, the other arguments are ignored, and data is
// sent directly to DDMS.
// If "fd" is >= 0, the output will be written to that file descriptor.
// Otherwise, "filename" is used to create an output file.
void DumpHeap(const char* filename, int fd, bool direct_to_ddms) {
  CHECK(filename != nullptr);
  Thread* self = Thread::Current();
  // Need to take a heap dump while GC isn't running. See the comment in Heap::VisitObjects().
  // Also we need the critical section to avoid visiting the same object twice. See b/34967844
  gc::ScopedGCCriticalSection gcs(self,
                                  gc::kGcCauseHprof,
                                  gc::kCollectorTypeHprof);
  ScopedSuspendAll ssa(__FUNCTION__, true /* long suspend */);
  Hprof hprof(filename, fd, direct_to_ddms);
  hprof.Dump();
}

可以看到在dump前,通过ScopedSuspendAll(构造函数中执行SuspendAll)执行了暂停所有java线程的操作,以防止在dump的过程中java堆发生变化,当dump结束后通过ScopedSuspendAll析构函数进行ResumeAll。

解决了第一个问题,接下来看第二个问题,既然要冻结所有线程,子线程异步处理是没有意义的,那么在子进程中处理呢?Android的内核是定制过的Linux, 而Linux fork子进程有一个著名的COW(Copy-on-write,写时复制)机制,即为了节省fork子进程的内存消耗和耗时,fork出的子进程并不会copy父进程的内存,而是和父进程共享内存空间。那么如何做到进程隔离呢,父子进程只在发生内存写入操作时,系统才会分配新的内存为写入方保留单独的拷贝,这就相当于子进程保留了fork瞬间时父进程的内存镜像,且后续父进程对内存的修改不会影响子进程,想到这里我们豁然开朗。说干就干,我们写了一个demo来验证这个思路,

很快就遇到了棘手的新问题:dump前需要暂停所有java线程而子进程只保留父进程执行fork操作的线程,在子进程中执行SuspendAll触发暂停是永远等不到其他线程返回结果的(详见thread_list.cc中行SuspendAll的实现,这里不展开讲了),经过仔细分析SuspendAll的过程,我们发现,可以先在主进程执行SuspendAll,使ThreadList中保存的所有线程状态为suspend,之后fork,子进程共享父进程的ThreadList全局变量,子进程可以欺骗虚拟机,使其以为子进程全部线程已经完成了暂停操作,接下来子进程就可以愉快的dump hprof了,而父进程可以立刻执行ResumeAll恢复运行。

这里有一个小技巧,SuspendAll没有对外暴露Java层的API,我们可以通过C层间接暴露的art::Dbg::SuspendVM来调用,dlsym拿到“_ZN3art3Dbg9SuspendVMEv”的地址调用即可,ResumeAll同理,注意这个函数在android 11以后已经被去除了,需要另行适配。Android 7之后对linker做了限制(即dlopen系统库失效),快手自研了kwai-linker组件,通过caller address替换和dl_iterate_phdr解析绕过了这一限制。
至此,我们完美解决了dump hprof冻结app的问题,用一张图总结:

1.ForkJvmHeapDumper  fork dump流程

            //topic 如何开辟子进程dump的 step1
            ForkJvmHeapDumper().run {
                dump(hprofFile.absolutePath)//开辟子进程dump
            }


public class ForkJvmHeapDumper extends HeapDumper {
    ...

    @Override
    public boolean dump(String path) {
        MonitorLog.i(TAG, "dump " + path);
        ...

        boolean dumpRes = false;
        try {
            MonitorLog.i(TAG, "before suspend and fork.");
            //topic 如何开辟子进程dump的 step3-1
            int pid = suspendAndFork();//pid为0,开辟的子进程,将去dump
            if (pid == 0) {
                // Child process
                Debug.dumpHprofData(path);
                exitProcess();//topic 如何开辟子进程dump的 step3-2
            } else if (pid > 0) {//主进程将会resumeAndWait
                // Parent process
                dumpRes = resumeAndWait(pid);//topic 如何开辟子进程dump的 step3-3
                MonitorLog.i(TAG, "notify from pid " + pid);
            }
        } catch (IOException e) {
            MonitorLog.e(TAG, "dump failed caused by " + e.toString());
            e.printStackTrace();
        }
        return dumpRes;
    }

    /**
     * Init before do dump. //topic 如何开辟子进程dump的 step2
     */
    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();//topic 如何开辟子进程dump的 step3-1

    /**
     * Resume the whole ART, and then wait child process to notify.
     *
     * @param pid pid of child process.
     */
    private native boolean resumeAndWait(int pid);//topic 如何开辟子进程dump的 step3-3

    /**
     * Exit current process.
     */
    private native void exitProcess();//topic 如何开辟子进程dump的 step3-2
}

2.hrof_dump.h 代码分析

 
#ifndef KOOM_HPROF_DUMP_H
#define KOOM_HPROF_DUMP_H
 
#include <android-base/macros.h>
 
#include <memory>
#include <string>
 
namespace kwai {
    namespace leak_monitor {
 
        ...
 
        class HprofDump {
        public:
            //获取HprofDump实例
            static HprofDump &GetInstance();
 
            //初始化
            void Initialize();
 
            //SuspendAndFork
            pid_t SuspendAndFork();
 
            //ResumeAndWait
            bool ResumeAndWait(pid_t pid);
 
        private:
            HprofDump();
 
            ~HprofDump() = default;
 
            //https://blog.youkuaiyun.com/u011157036/article/details/45247965
            //有时候,进行类体设计时,会发现某个类的对象是独一无二的,没有完全相同的对象,也就是对该类对象做副本没有任何意义.
            //因此,需要限制编译器自动生动的拷贝构造函数和赋值构造函数.一般参用下面的宏定义的方式进行限制,代码如下:
            DISALLOW_COPY_AND_ASSIGN(HprofDump);
            //初始化完成
            bool init_done_;
            //api版本
            int android_api_;
 
 
            /**
             * Function pointer for ART <= Android Q
             * 方法指针 ART小于等于Android q的
             */
             //suspend vm的方法
            // art::Dbg::SuspendVM
            void (*suspend_vm_fnc_)();
 
            //resume vm的方法
            // art::Dbg::ResumeVM
            void (*resume_vm_fnc_)();
 
            /**
             * Function pointer for ART Android R
             * todo 方法指针 art android R的忽略先
             */
           
        };
 
    }  // namespace leak_monitor
}  // namespace kwai
#endif  // KOOM_HPROF_DUMP_H

3.hprof_dump.cpp 原理

#undef LOG_TAG
#define LOG_TAG "HprofDump"

using namespace kwai::linker;

namespace kwai {
    namespace leak_monitor {
        ......

        //初始化
        void HprofDump::Initialize() {
            if (init_done_ || android_api_ < __ANDROID_API_L__) {
                return;
            }

            //获取libart的handle手柄,把手
            void *handle = kwai::linker::DlFcn::dlopen("libart.so", RTLD_NOW);
            KCHECKV(handle)

            if (android_api_ < __ANDROID_API_R__) {
                //获取SuspendVMEv方法指针
                suspend_vm_fnc_ =
                        (void (*)()) DlFcn::dlsym(handle, "_ZN3art3Dbg9SuspendVMEv");
                KFINISHV_FNC(suspend_vm_fnc_, DlFcn::dlclose, handle)

                //获取ResumeVMEv方法指针,resume vm的方法
                resume_vm_fnc_ = (void (*)()) kwai::linker::DlFcn::dlsym(
                        handle, "_ZN3art3Dbg8ResumeVMEv");
                KFINISHV_FNC(resume_vm_fnc_, DlFcn::dlclose, handle)
            }
            if (android_api_ == __ANDROID_API_R__) {
                //R 的忽略先... 
            }
            DlFcn::dlclose(handle);
            init_done_ = true;
        }

        pid_t HprofDump::SuspendAndFork() {
            KCHECKI(init_done_)

            if (android_api_ < __ANDROID_API_R__) {
                suspend_vm_fnc_();      //suspend虚拟机
            }
            if (android_api_ == __ANDROID_API_R__) {
                 //R 的忽略先... 
            }

            pid_t pid = fork();         //fork子进程
            if (pid == 0) {             //如果是子进程
                // Set timeout for child process
                alarm(60);              //子进程60s之内退出
                prctl(PR_SET_NAME, "forked-dump-process");//设置子进程名字
            }
            return pid;
        }

        bool HprofDump::ResumeAndWait(pid_t pid) {
            KCHECKB(init_done_)     //检测init_done_是否true,如果不是true,return;

            if (android_api_ < __ANDROID_API_R__) {
                resume_vm_fnc_();   //resume vm的方法
            }
            if (android_api_ == __ANDROID_API_R__) {
                //R 的忽略先... 
            }
            int status;
            for (;;) {
                if (waitpid(pid, &status, 0) != -1 || errno != EINTR) {
                    //WIFEXITED 函数作用 Returns true if the process exited normally.
                    //status为0正常退出,dump成功
                    if (!WIFEXITED(status)) {
                        ALOGE("Child process %d exited with status %d, terminated by signal %d",
                              pid, WEXITSTATUS(status), WTERMSIG(status));
                        return false;
                    }
                    return true;
                }
                return false;
            }
        }

    }  // namespace leak_monitor
}  // namespace kwai

三.解决hprof文件过大

我们将问题拆解:

  • hprof存的内容都是些什么?数据如何组织的?哪些可以裁掉?
  • 内存中的数据结构和hprof文件二进制协议的映射关系?
  • 如何裁剪?

想要了解hprof的数据组织方式,这里简要介绍一下核心内容:

文件按byte by byte顺序存储,u1,u2,u4分别代表1字节,2字节,4字节。
总体分为两部分,Header和Record,Header记录hprof的元信息,Record分很多条目,每一条有一个单独的TAG代表类型。
我们关注的Record类型主要是HEAP DUMP,其中又分五个子类,分别为GC ROOT、CLASS DUMP、INSTANCE DUMP、OBJECT ARRAY DUMP、PRIMITIVE ARRAY DUMP。图13以PRIMITIVE ARRAY DUMP(基本类型数组)为例展示Record中包含的信息,其他类型请查阅官方文档。内存中绝大部分数据是PRIMITIVE ARRAY DUMP,通常占据80%以上,而我们分析OOM只关系对象的大小和引用关系,并不关心内容,因此这部分是我们裁剪的突破口。

Android对数据类型做了扩展,增加了一些GC ROOT

  // Android.
  HPROF_HEAP_DUMP_INFO = 0xfe,
  HPROF_ROOT_INTERNED_STRING = 0x89,
  HPROF_ROOT_FINALIZING = 0x8a,  // Obsolete.
  HPROF_ROOT_DEBUGGER = 0x8b,
  HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,  // Obsolete.
  HPROF_ROOT_VM_INTERNAL = 0x8d,
  HPROF_ROOT_JNI_MONITOR = 0x8e,
  HPROF_UNREACHABLE = 0x90,  // Obsolete.
  HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,  // Obsolete.

还有一个HEAP_DUMP_INFO,这里面保存的是堆空间(heap space)的类型,Android对堆空间做了划分,我们只关注HPROF_HEAP_APP即可,其余也是可以裁剪掉的,可以参考Android Studio中Memory Profiler的处理[3]。

enum HprofHeapId {
  HPROF_HEAP_DEFAULT = 0,
  HPROF_HEAP_ZYGOTE = 'Z',
  HPROF_HEAP_APP = 'A',
  HPROF_HEAP_IMAGE = 'I',
};

 接下来讨论如何裁剪,裁剪有两种办法,第一种是在dump完成后的hprof文件基础上裁剪,性能比较差,对磁盘空间要求也比较高,第二种是在dump的过程中实时裁剪,我们自然想要实现第二种。看一下Record写入的过程,先执行StartNewRecord,然后通过AddU1/U4/U8写入内存buffer,最后执行EndRecord将buffer写入文件。

void StartNewRecord(uint8_t tag, uint32_t time) {
    if (length_ > 0) {
      EndRecord();
    }
    DCHECK_EQ(length_, 0U);
    AddU1(tag);
    AddU4(time);
    AddU4(0xdeaddead);  // Length, replaced on flush.
    started_ = true;
}

void EndRecord() {
  // Replace length in header.
  if (started_) {
    UpdateU4(sizeof(uint8_t) + sizeof(uint32_t),
             length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t));
  }

  HandleEndRecord();

  sum_length_ += length_;
  max_length_ = std::max(max_length_, length_);
  length_ = 0;
  started_ = false;
}
void HandleFlush(const uint8_t* buffer, size_t length) override {
    if (!errors_) {
      errors_ = !fp_->WriteFully(buffer, length);
    }
}

这个过程中有两个hook点可以选择,一是hook AddUx,在写入buffer的过程中裁剪,二是hook write,在写入文件过程中裁剪。最终我们选择了方案二,理由是AddUx调用比较频繁,判断逻辑复杂容易出现兼容性问题,而write是public API,且只在Record写入文件的时候调用一次,厂商不会魔改相关实现,从hook原理上来讲,hook外部调用的PLT/GOT hook也比hook内部调用的inline hook要稳定得多。

用一张图总结裁剪的流程:

 1.hprof_strip.h 源码解析


#ifndef KOOM_HPROF_STRIP_H
#define KOOM_HPROF_STRIP_H

#include <android-base/macros.h>

#include <memory>
#include <string>

namespace kwai {
    namespace leak_monitor {

        class HprofStrip {
        public:
            //获取HprofStrip实例
            static HprofStrip &GetInstance();

            //init方法
            static void HookInit();

            //hook open的方法,flags todo
            int HookOpenInternal(const char *path_name, int flags, ...);

            //hook write的方法
            /**
             *
             * @param fd 文件描述符
             * @param buf 一段内存,开始指针
             * @param count 这段内存字节数
             * @return 看着返回也是count
             */
            ssize_t HookWriteInternal(int fd, const void *buf, size_t count);

            //是否hook成功
            bool IsHookSuccess() const;

            //设置hprof文件名字
            void SetHprofName(const char *hprof_name);

        private:
            //构造函数
            HprofStrip();

            //析构函数
            ~HprofStrip() = default;
            //https://blog.youkuaiyun.com/u011157036/article/details/45247965
            //有时候,进行类体设计时,会发现某个类的对象是独一无二的,没有完全相同的对象,也就是对该类对象做副本没有任何意义.
            //因此,需要限制编译器自动生动的拷贝构造函数和赋值构造函数.一般参用下面的宏定义的方式进行限制,代码如下:
            DISALLOW_COPY_AND_ASSIGN(HprofStrip);

            //从buf的index位置获取short
            static int GetShortFromBytes(const unsigned char *buf, int index);

            //从buf的index位置获取int
            static int GetIntFromBytes(const unsigned char *buf, int index);

            //获取相关类型占多少字节
            static int GetByteSizeFromType(unsigned char basic_type);

            /**
             * 递归处理一段buf,按tag来处理
             * @param buf 处理的数据指针
             * @param first_index 开始处理位置
             * @param max_len 这段数据的字节数
             * @param heap_serial_no是 heap_serial_num_当前的值,我们关注的Record类型主要是HEAP DUMP,
             *        heap_serial_num_表示HEAP DUMP的数量
             * @param array_serial_no 处理的基本类型数组的个数,基本类型数组tag是HPROF_PRIMITIVE_ARRAY_DUMP
             * @return
             */
            int ProcessHeap(const void *buf, int first_index, int max_len,
                            int heap_serial_no, int array_serial_no);

            //重置
            void reset();

            //文件描述符
            int hprof_fd_;
            //裁剪字节计数
            int strip_bytes_sum_;
            //我们关注的Record类型主要是HEAP DUMP,其中又分五个子类,分别为GC ROOT、CLASS DUMP、INSTANCE DUMP、OBJECT ARRAY DUMP、PRIMITIVE ARRAY DUMP。
            //HPROF_TAG_HEAP_DUMP HPROF_TAG_HEAP_DUMP_SEGMENT 的个数
            int heap_serial_num_;
            //hook的write调用了多少次
            int hook_write_serial_num_;
            //裁剪次数计数,和strip_index_list_pair_数组结合使用
            int strip_index_;

            //是否hook成功
            bool is_hook_success_;
            //是否是系统heap,heap_type == HPROF_HEAP_ZYGOTE || heap_type == HPROF_HEAP_IMAGE,这俩需要裁剪
            bool is_current_system_heap_;
            //hprof名字
            std::string hprof_name_;
            //Strip裁剪区域数组大小 2^16 * 2 * 2 + 2
            static constexpr int kStripListLength = 65536 * 2 * 2 + 2;
            //每两个为一组,第一个值为开始位置,第二个值为结束的位置,记录裁剪区域
            int strip_index_list_pair_[kStripListLength];
        };

    }  // namespace leak_monitor
}  // namespace kwai

#endif  // KOOM_HPROF_STRIP_H

2.hprof_strip.cpp 裁剪源码分析


#include <android/log.h>
#include <fcntl.h>
#include <hprof_strip.h>
#include <kwai_util/kwai_macros.h>
#include <unistd.h>
#include <xhook.h>

#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <list>

#define LOG_TAG "HprofCrop"

namespace kwai {
    namespace leak_monitor {

        enum HprofTag {
            HPROF_TAG_STRING = 0x01,
            HPROF_TAG_LOAD_CLASS = 0x02,
            HPROF_TAG_UNLOAD_CLASS = 0x03,
            HPROF_TAG_STACK_FRAME = 0x04,
            HPROF_TAG_STACK_TRACE = 0x05,
            HPROF_TAG_ALLOC_SITES = 0x06,
            HPROF_TAG_HEAP_SUMMARY = 0x07,
            HPROF_TAG_START_THREAD = 0x0A,
            HPROF_TAG_END_THREAD = 0x0B,
            HPROF_TAG_HEAP_DUMP = 0x0C,         //关注的Record类型主要是HEAP DUMP
            HPROF_TAG_HEAP_DUMP_SEGMENT = 0x1C, //关注的Record类型主要是HEAP DUMP
            HPROF_TAG_HEAP_DUMP_END = 0x2C,
            HPROF_TAG_CPU_SAMPLES = 0x0D,
            HPROF_TAG_CONTROL_SETTINGS = 0x0E,
        };

        enum HprofHeapTag {
            // Traditional.
            HPROF_ROOT_UNKNOWN = 0xFF,
            HPROF_ROOT_JNI_GLOBAL = 0x01,
            HPROF_ROOT_JNI_LOCAL = 0x02,
            HPROF_ROOT_JAVA_FRAME = 0x03,
            HPROF_ROOT_NATIVE_STACK = 0x04,
            HPROF_ROOT_STICKY_CLASS = 0x05,
            HPROF_ROOT_THREAD_BLOCK = 0x06,
            HPROF_ROOT_MONITOR_USED = 0x07,
            HPROF_ROOT_THREAD_OBJECT = 0x08,
            HPROF_CLASS_DUMP = 0x20,
            HPROF_INSTANCE_DUMP = 0x21,
            HPROF_OBJECT_ARRAY_DUMP = 0x22,
            HPROF_PRIMITIVE_ARRAY_DUMP = 0x23,

            // Android.
            HPROF_HEAP_DUMP_INFO = 0xfe,
            HPROF_ROOT_INTERNED_STRING = 0x89,
            HPROF_ROOT_FINALIZING = 0x8a,  // Obsolete.
            HPROF_ROOT_DEBUGGER = 0x8b,
            HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,  // Obsolete.
            HPROF_ROOT_VM_INTERNAL = 0x8d,
            HPROF_ROOT_JNI_MONITOR = 0x8e,
            HPROF_UNREACHABLE = 0x90,                  // Obsolete.
            HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,  // Obsolete.
        };

        enum HprofBasicType {
            hprof_basic_object = 2,
            hprof_basic_boolean = 4,
            hprof_basic_char = 5,
            hprof_basic_float = 6,
            hprof_basic_double = 7,
            hprof_basic_byte = 8,
            hprof_basic_short = 9,
            hprof_basic_int = 10,
            hprof_basic_long = 11,
        };

        enum HprofHeapId {
            HPROF_HEAP_DEFAULT = 0,
            HPROF_HEAP_ZYGOTE = 'Z',
            HPROF_HEAP_APP = 'A',
            HPROF_HEAP_IMAGE = 'I',
        };

        enum HprofTagBytes {
            OBJECT_ID_BYTE_SIZE = 4,
            JNI_GLOBAL_REF_ID_BYTE_SIZE = 4,
            CLASS_ID_BYTE_SIZE = 4,
            CLASS_LOADER_ID_BYTE_SIZE = 4,
            INSTANCE_SIZE_BYTE_SIZE = 4,
            CONSTANT_POOL_LENGTH_BYTE_SIZE = 2,
            STATIC_FIELD_LENGTH_BYTE_SIZE = 2,
            INSTANCE_FIELD_LENGTH_BYTE_SIZE = 2,
            STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE = 4,
            RECORD_TIME_BYTE_SIZE = 4,
            RECORD_LENGTH_BYTE_SIZE = 4,
            STRING_ID_BYTE_SIZE = 4,

            HEAP_TAG_BYTE_SIZE = 1,
            THREAD_SERIAL_BYTE_SIZE = 4,
            CONSTANT_POLL_INDEX_BYTE_SIZE = 2,
            BASIC_TYPE_BYTE_SIZE = 1,
            HEAP_TYPE_BYTE_SIZE = 4,
        };

        static constexpr int U4 = 4;

        //从buf的index位置获取short
        ALWAYS_INLINE int HprofStrip::GetShortFromBytes(const unsigned char *buf,
                                                        int index) {
            return (buf[index] << 8u) + buf[index + 1];
        }

        //从buf的index位置获取int
        ALWAYS_INLINE int HprofStrip::GetIntFromBytes(const unsigned char *buf,
                                                      int index) {
            return (buf[index] << 24u) + (buf[index + 1] << 16u) +
                   (buf[index + 2] << 8u) + buf[index + 3];
        }

        //获取相关类型占多少字节
        int HprofStrip::GetByteSizeFromType(unsigned char basic_type) {
            switch (basic_type) {
                case hprof_basic_boolean:
                case hprof_basic_byte:
                    return 1;
                case hprof_basic_char:
                case hprof_basic_short:
                    return 2;
                case hprof_basic_float:
                case hprof_basic_int:
                case hprof_basic_object:
                    return 4;
                case hprof_basic_long:
                case hprof_basic_double:
                    return 8;
                default:
                    return 0;
            }
        }

        /**
         * 递归处理一段buf,按tag来处理
         * @param buf 处理的数据指针
         * @param first_index 开始处理位置
         * @param max_len 这段数据的字节数
         * @param heap_serial_no是 heap_serial_num_当前的值,我们关注的Record类型主要是HEAP DUMP,
         *        heap_serial_num_表示HEAP DUMP的数量
         * @param array_serial_no 表示处理的基本类型数组的个数,基本类型数组tag是HPROF_PRIMITIVE_ARRAY_DUMP
         * @return
         */
        int HprofStrip::ProcessHeap(const void *buf, int first_index, int max_len,
                                    int heap_serial_no, int array_serial_no) {
            //到达最后一个位置就返回array_serial_no,表示处理的基本类型数组的个数
            //基本类型数组tag是HPROF_PRIMITIVE_ARRAY_DUMP
            if (first_index >= max_len) {
                return array_serial_no;
            }

            const unsigned char subtag = ((unsigned char *) buf)[first_index];
            //我们关注的Record类型主要是HEAP DUMP,其中又分五个子类,
            // 分别为GC ROOT、CLASS DUMP、INSTANCE DUMP、OBJECT ARRAY DUMP、PRIMITIVE ARRAY DUMP。
            switch (subtag) {
                /**
                 * __ AddU1(heap_tag);
                 * __ AddObjectId(obj);
                 *
                 */
                case HPROF_ROOT_UNKNOWN:
                case HPROF_ROOT_STICKY_CLASS:
                case HPROF_ROOT_MONITOR_USED:
                case HPROF_ROOT_INTERNED_STRING:
                case HPROF_ROOT_DEBUGGER:
                case HPROF_ROOT_VM_INTERNAL: {
                    //递归处理,first_index变为first_index+AddU1+AddObjectId
                    array_serial_no = ProcessHeap(
                            buf, first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE, max_len,
                            heap_serial_no, array_serial_no);
                }
                    break;

                case HPROF_ROOT_JNI_GLOBAL: {
                    /**
                     *  __ AddU1(heap_tag);
                     *  __ AddObjectId(obj);
                     *  __ AddJniGlobalRefId(jni_obj);
                     *
                     */
                    //递归处理,first_index变为first_index+AddU1+AddObjectId+AddJniGlobalRefId
                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        JNI_GLOBAL_REF_ID_BYTE_SIZE,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(heap_tag);
                     * __ AddObjectId(obj);
                     * __ AddU4(thread_serial);
                     * __ AddU4((uint32_t)-1);
                     */
                case HPROF_ROOT_JNI_LOCAL:
                case HPROF_ROOT_JAVA_FRAME:
                case HPROF_ROOT_JNI_MONITOR: {
                    //递归处理,first_index变为first_index+AddU1+AddObjectId+AddU4+AddU4
                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        THREAD_SERIAL_BYTE_SIZE + U4 /*占位*/,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(heap_tag);
                     * __ AddObjectId(obj);
                     * __ AddU4(thread_serial);
                     */
                case HPROF_ROOT_NATIVE_STACK:
                case HPROF_ROOT_THREAD_BLOCK: {
                    //递归处理,first_index变为first_index+AddU1+AddObjectId+AddU4
                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        THREAD_SERIAL_BYTE_SIZE,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(heap_tag);
                     * __ AddObjectId(obj);
                     * __ AddU4(thread_serial);
                     * __ AddU4((uint32_t)-1);    // xxx
                     */
                case HPROF_ROOT_THREAD_OBJECT: {
                    //递归处理,first_index变为first_index+AddU1+AddObjectId+AddU4+AddU4
                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        THREAD_SERIAL_BYTE_SIZE + U4 /*占位*/,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(HPROF_CLASS_DUMP);
                     * __ AddClassId(LookupClassId(klass));
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(klass));
                     * __ AddClassId(LookupClassId(klass->GetSuperClass().Ptr()));
                     * __ AddObjectId(klass->GetClassLoader().Ptr());
                     * __ AddObjectId(nullptr);    // no signer
                     * __ AddObjectId(nullptr);    // no prot domain
                     * __ AddObjectId(nullptr);    // reserved
                     * __ AddObjectId(nullptr);    // reserved
                     * __ AddU4(0); 或 __ AddU4(sizeof(mirror::String)); 或 __ AddU4(0); 或 __
                     * AddU4(klass->GetObjectSize());  // instance size
                     * __ AddU2(0);  // empty const pool
                     * __ AddU2(dchecked_integral_cast<uint16_t>(static_fields_reported));
                     * static_field_writer(class_static_field, class_static_field_name_fn);
                     */
                case HPROF_CLASS_DUMP: {
                    /**
                     *  u2
                        size of constant pool and number of records that follow:
                            u2
                            constant pool index
                            u1
                            type of entry: (See Basic Type)
                            value
                            value of entry (u1, u2, u4, or u8 based on type of entry)
                     */
                    int constant_pool_index =
                            first_index + HEAP_TAG_BYTE_SIZE /*tag*/
                            + CLASS_ID_BYTE_SIZE + STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE +
                            CLASS_ID_BYTE_SIZE /*super*/ + CLASS_LOADER_ID_BYTE_SIZE +
                            OBJECT_ID_BYTE_SIZE    // Ignored: Signeres ID.
                            + OBJECT_ID_BYTE_SIZE  // Ignored: Protection domain ID.
                            + OBJECT_ID_BYTE_SIZE  // RESERVED.
                            + OBJECT_ID_BYTE_SIZE  // RESERVED.
                            + INSTANCE_SIZE_BYTE_SIZE;
                    //读取常量大小
                    int constant_pool_size =
                            GetShortFromBytes((unsigned char *) buf, constant_pool_index);
                    constant_pool_index += CONSTANT_POOL_LENGTH_BYTE_SIZE;
                    for (int i = 0; i < constant_pool_size; ++i) {
                        unsigned char type = ((
                                unsigned char *) buf)[constant_pool_index +
                                                      CONSTANT_POLL_INDEX_BYTE_SIZE /*pool index*/];
                        constant_pool_index += CONSTANT_POLL_INDEX_BYTE_SIZE /*poll index*/
                                               + BASIC_TYPE_BYTE_SIZE /*type*/ +
                                               GetByteSizeFromType(type);
                    }

                    /**
                     * u2 Number of static fields:
                         ID
                         static field name string ID
                         u1
                         type of field: (See Basic Type)
                         value
                         value of entry (u1, u2, u4, or u8 based on type of field)
                     */
                    //读取static fields大小
                    int static_fields_index = constant_pool_index;
                    int static_fields_size =
                            GetShortFromBytes((unsigned char *) buf, static_fields_index);
                    static_fields_index += STATIC_FIELD_LENGTH_BYTE_SIZE;
                    for (int i = 0; i < static_fields_size; ++i) {
                        unsigned char type =
                                ((unsigned char *)
                                        buf)[static_fields_index + STRING_ID_BYTE_SIZE /*ID*/];
                        static_fields_index += STRING_ID_BYTE_SIZE /*string ID*/ +
                                               BASIC_TYPE_BYTE_SIZE /*type*/
                                               + GetByteSizeFromType(type);
                    }

                    /**
                     * u2
                       Number of instance fields (not including super class's)
                            ID
                            field name string ID
                            u1
                            type of field: (See Basic Type)
                     */
                    int instance_fields_index = static_fields_index;
                    //获取实例
                    int instance_fields_size =
                            GetShortFromBytes((unsigned char *) buf, instance_fields_index);
                    instance_fields_index += INSTANCE_FIELD_LENGTH_BYTE_SIZE;
                    instance_fields_index +=
                            (BASIC_TYPE_BYTE_SIZE + STRING_ID_BYTE_SIZE) * instance_fields_size;

                    array_serial_no = ProcessHeap(buf, instance_fields_index, max_len,
                                                  heap_serial_no, array_serial_no);
                }

                    break;

                    /**
                     *__ AddU1(HPROF_INSTANCE_DUMP);
                     * __ AddObjectId(obj);
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
                     * __ AddClassId(LookupClassId(klass));
                     *
                     * __ AddU4(0x77777777);//length
                     *
                     * ***
                     */
                case HPROF_INSTANCE_DUMP: {
                    int instance_dump_index =
                            first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                            STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE + CLASS_ID_BYTE_SIZE;
                    int instance_size =
                            GetIntFromBytes((unsigned char *) buf, instance_dump_index);

                    // 裁剪掉system space
                    if (is_current_system_heap_) {
                        strip_index_list_pair_[strip_index_ * 2] = first_index;
                        strip_index_list_pair_[strip_index_ * 2 + 1] =
                                instance_dump_index + U4 /*占位*/ + instance_size;
                        strip_index_++;

                        strip_bytes_sum_ +=
                                instance_dump_index + U4 /*占位*/ + instance_size - first_index;
                    }

                    array_serial_no =
                            ProcessHeap(buf, instance_dump_index + U4 /*占位*/ + instance_size,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     * __ AddU1(HPROF_OBJECT_ARRAY_DUMP);
                     * __ AddObjectId(obj);
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
                     * __ AddU4(length);
                     * __ AddClassId(LookupClassId(klass));
                     *
                     * // Dump the elements, which are always objects or null.
                     * __ AddIdList(obj->AsObjectArray<mirror::Object>().Ptr());
                     */
                case HPROF_OBJECT_ARRAY_DUMP: {
                    int length = GetIntFromBytes((unsigned char *) buf,
                                                 first_index + HEAP_TAG_BYTE_SIZE +
                                                 OBJECT_ID_BYTE_SIZE +
                                                 STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE);

                    // 裁剪掉system space
                    if (is_current_system_heap_) {
                        strip_index_list_pair_[strip_index_ * 2] = first_index;
                        strip_index_list_pair_[strip_index_ * 2 + 1] =
                                first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE + U4 /*Length*/
                                + CLASS_ID_BYTE_SIZE + U4 /*Id*/ * length;
                        strip_index_++;

                        strip_bytes_sum_ += HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                            STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE + U4 /*Length*/
                                            + CLASS_ID_BYTE_SIZE + U4 /*Id*/ * length;
                    }

                    array_serial_no =
                            ProcessHeap(buf,
                                        first_index + HEAP_TAG_BYTE_SIZE + OBJECT_ID_BYTE_SIZE +
                                        STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE + U4 /*Length*/
                                        + CLASS_ID_BYTE_SIZE + U4 /*Id*/ * length,
                                        max_len, heap_serial_no, array_serial_no);
                }
                    break;

                    /**
                     *
                     * __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
                     * __ AddClassStaticsId(klass);
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(klass));
                     * __ AddU4(java_heap_overhead_size - 4);
                     * __ AddU1(hprof_basic_byte);
                     * for (size_t i = 0; i < java_heap_overhead_size - 4; ++i) {
                     *      __ AddU1(0);
                     * }
                     *
                     * // obj is a primitive array.
                     * __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
                     * __ AddObjectId(obj);
                     * __ AddStackTraceSerialNumber(LookupStackTraceSerialNumber(obj));
                     * __ AddU4(length);
                     * __ AddU1(t);
                     * // Dump the raw, packed element values.
                     * if (size == 1) {
                     *      __ AddU1List(reinterpret_cast<const
                     * uint8_t*>(obj->GetRawData(sizeof(uint8_t), 0)), length); } else if
                     * (size == 2) {
                     *      __ AddU2List(reinterpret_cast<const
                     * uint16_t*>(obj->GetRawData(sizeof(uint16_t), 0)), length); } else if
                     * (size == 4) {
                     *      __ AddU4List(reinterpret_cast<const
                     * uint32_t*>(obj->GetRawData(sizeof(uint32_t), 0)), length); } else if
                     * (size == 8) {
                     *      __ AddU8List(reinterpret_cast<const
                     * uint64_t*>(obj->GetRawData(sizeof(uint64_t), 0)), length);
                     * }
                     */
                case HPROF_PRIMITIVE_ARRAY_DUMP: {
                    int primitive_array_dump_index = first_index + HEAP_TAG_BYTE_SIZE /*tag*/
                                                     + OBJECT_ID_BYTE_SIZE +
                                                     STACK_TRACE_SERIAL_NUMBER_BYTE_SIZE;
                    int length =
                            GetIntFromBytes((unsigned char *) buf, primitive_array_dump_index);
                    primitive_array_dump_index += U4 /*Length*/;

                    // 裁剪掉基本类型数组,无论是否在system space都进行裁剪
                    // 区别是数组左坐标,app space时带数组元信息(类型、长度)方便回填 todo
                    if (is_current_system_heap_) {
                        strip_index_list_pair_[strip_index_ * 2] = first_index;
                    } else {
                        strip_index_list_pair_[strip_index_ * 2] =
                                primitive_array_dump_index + BASIC_TYPE_BYTE_SIZE /*value type*/;
                    }
                    array_serial_no++;

                    int value_size = GetByteSizeFromType(
                            ((unsigned char *) buf)[primitive_array_dump_index]);
                    primitive_array_dump_index +=
                            BASIC_TYPE_BYTE_SIZE /*value type*/ + value_size * length;

                    // 数组右坐标
                    strip_index_list_pair_[strip_index_ * 2 + 1] = primitive_array_dump_index;

                    // app space时,不修改长度因为回填数组时会补齐 todo
                    if (is_current_system_heap_) {
                        strip_bytes_sum_ += primitive_array_dump_index - first_index;
                    }
                    strip_index_++;

                    array_serial_no = ProcessHeap(buf, primitive_array_dump_index, max_len,
                                                  heap_serial_no, array_serial_no);
                }
                    break;

                    // Android.
                case HPROF_HEAP_DUMP_INFO: {
                    const unsigned char heap_type =
                            ((unsigned char *) buf)[first_index + HEAP_TAG_BYTE_SIZE + 3];
                    is_current_system_heap_ =
                            (heap_type == HPROF_HEAP_ZYGOTE || heap_type == HPROF_HEAP_IMAGE);

                    if (is_current_system_heap_) {
                        strip_index_list_pair_[strip_index_ * 2] = first_index;
                        strip_index_list_pair_[strip_index_ * 2 + 1] =
                                first_index + HEAP_TAG_BYTE_SIZE /*TAG*/
                                + HEAP_TYPE_BYTE_SIZE            /*heap type*/
                                + STRING_ID_BYTE_SIZE /*string id*/;
                        strip_index_++;
                        strip_bytes_sum_ += HEAP_TAG_BYTE_SIZE    /*TAG*/
                                            + HEAP_TYPE_BYTE_SIZE /*heap type*/
                                            + STRING_ID_BYTE_SIZE /*string id*/;
                    }

                    array_serial_no = ProcessHeap(buf,
                                                  first_index + HEAP_TAG_BYTE_SIZE /*TAG*/
                                                  + HEAP_TYPE_BYTE_SIZE /*heap type*/
                                                  + STRING_ID_BYTE_SIZE /*string id*/,
                                                  max_len, heap_serial_no, array_serial_no);
                }
                    break;

                case HPROF_ROOT_FINALIZING:                // Obsolete.
                case HPROF_ROOT_REFERENCE_CLEANUP:         // Obsolete.
                case HPROF_UNREACHABLE:                    // Obsolete.
                case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP: {  // Obsolete.
                    array_serial_no = ProcessHeap(buf, first_index + HEAP_TAG_BYTE_SIZE,
                                                  max_len, heap_serial_no, array_serial_no);
                }
                    break;

                default:
                    break;
            }
            return array_serial_no;
        }

        static int HookOpen(const char *pathname, int flags, ...) {
            va_list ap;
            va_start(ap, flags);
            int fd = HprofStrip::GetInstance().HookOpenInternal(pathname, flags, ap);
            va_end(ap);
            return fd;
        }

        int HprofStrip::HookOpenInternal(const char *path_name, int flags, ...) {
            va_list ap;
            va_start(ap, flags);
            int fd = open(path_name, flags, ap);
            va_end(ap);

            if (hprof_name_.empty()) {      //hprof_name_是我们的hprof
                return fd;
            }

            if (path_name != nullptr && strstr(path_name, hprof_name_.c_str())) {
                hprof_fd_ = fd;             //设置hprof_fd_
                is_hook_success_ = true;    //设置is_hook_success_为true
            }
            return fd;
        }

        static ssize_t HookWrite(int fd, const void *buf, size_t count) {
            return HprofStrip::GetInstance().HookWriteInternal(fd, buf, count);
        }

        void HprofStrip::reset() {
            strip_index_ = 0;
            strip_bytes_sum_ = 0;
        }

        ssize_t HprofStrip::HookWriteInternal(int fd, const void *buf, size_t count) {
            if (fd != hprof_fd_) {
                //如果是我们的hprof_fd_
                return write(fd, buf, count);
            }

            // 每次hook_write,初始化重置
            reset();

            const unsigned char tag = ((unsigned char *) buf)[0];
            // 删除掉无关record tag类型匹配,只匹配heap相关提高性能
            switch (tag) {
                // 我们关注的Record类型主要是HEAP DUMP,其中又分五个子类,
                // 分别为GC ROOT、CLASS DUMP、INSTANCE DUMP、OBJECT ARRAY DUMP、PRIMITIVE ARRAY DUMP。
                case HPROF_TAG_HEAP_DUMP:
                case HPROF_TAG_HEAP_DUMP_SEGMENT: {
                    ProcessHeap(
                            buf,
                            HEAP_TAG_BYTE_SIZE + RECORD_TIME_BYTE_SIZE + RECORD_LENGTH_BYTE_SIZE,
                            count, heap_serial_num_, 0);
                    heap_serial_num_++;
                }
                    break;
                default:
                    break;
            }

            // 根据裁剪掉的zygote space和image space更新length
            int record_length;
            if (tag == HPROF_TAG_HEAP_DUMP || tag == HPROF_TAG_HEAP_DUMP_SEGMENT) {
                record_length = GetIntFromBytes((unsigned char *) buf,
                                                HEAP_TAG_BYTE_SIZE + RECORD_TIME_BYTE_SIZE);
                record_length -= strip_bytes_sum_;//record_length删除了字节数
                int index = HEAP_TAG_BYTE_SIZE + RECORD_TIME_BYTE_SIZE;
                ((unsigned char *) buf)[index] =//修改record_length
                        (unsigned char) (((unsigned int) record_length & 0xff000000u) >> 24u);
                ((unsigned char *) buf)[index + 1] =
                        (unsigned char) (((unsigned int) record_length & 0x00ff0000u) >> 16u);
                ((unsigned char *) buf)[index + 2] =
                        (unsigned char) (((unsigned int) record_length & 0x0000ff00u) >> 8u);
                ((unsigned char *) buf)[index + 3] =
                        (unsigned char) ((unsigned int) record_length & 0x000000ffu);
            }

            size_t total_write = 0;
            int start_index = 0;
            for (int i = 0; i < strip_index_; i++) {
                // 将裁剪掉的区间,通过写时过滤掉
                void *write_buf = (void *) ((unsigned char *) buf + start_index);
                auto write_len = (size_t) (strip_index_list_pair_[i * 2] - start_index);
                if (write_len > 0) {
                    total_write += write(fd, write_buf, write_len);
                } else if (write_len < 0) {
                    __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
                                        "HookWrite array i:%d writeLen<0:%zu", i, write_len);
                }
                start_index = strip_index_list_pair_[i * 2 + 1];
            }
            auto write_len = (size_t) (count - start_index);
            if (write_len > 0) {
                void *write_buf = (void *) ((unsigned char *) buf + start_index);
                total_write += write(fd, write_buf, count - start_index);
            }

            hook_write_serial_num_++;

            if (total_write != count) {//说明裁剪发生了
                __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
                                    "hook write, hprof strip happens");
            }

            return count;
        }

        void HprofStrip::HookInit() {
            //使用xhook hook函数
            xhook_enable_debug(0);
            /**
             *
             * android 7.x,write方法在libc.so中
             * android 8-9,write方法在libart.so中
             * android 10,write方法在libartbase.so中
             * libbase.so是一个保险操作,防止前面2个so里面都hook不到(:
             *
             * android 7-10版本,open方法都在libart.so中
             * libbase.so与libartbase.so,为保险操作
             */
            xhook_register("libart.so", "open", (void *) HookOpen, nullptr);
            xhook_register("libbase.so", "open", (void *) HookOpen, nullptr);
            xhook_register("libartbase.so", "open", (void *) HookOpen, nullptr);

            xhook_register("libc.so", "write", (void *) HookWrite, nullptr);
            xhook_register("libart.so", "write", (void *) HookWrite, nullptr);
            xhook_register("libbase.so", "write", (void *) HookWrite, nullptr);
            xhook_register("libartbase.so", "write", (void *) HookWrite, nullptr);

            xhook_refresh(0);
            xhook_clear();
        }

        //返回HprofStrip实例
        HprofStrip &HprofStrip::GetInstance() {
            static HprofStrip hprof_strip;
            return hprof_strip;
        }

        //构造函数
        HprofStrip::HprofStrip()
                : hprof_fd_(-1),
                  strip_bytes_sum_(0),
                  heap_serial_num_(0),
                  hook_write_serial_num_(0),
                  strip_index_(0),
                  is_hook_success_(false),
                  is_current_system_heap_(false) {
            //初始化strip_index_list_pair_
            std::fill(strip_index_list_pair_,
                      strip_index_list_pair_ + arraysize(strip_index_list_pair_), 0);
        }

        //返回is_hook_success_
        ALWAYS_INLINE bool HprofStrip::IsHookSuccess() const {
            return is_hook_success_;
        }

        //设置hprof名字
        void HprofStrip::SetHprofName(const char *hprof_name) {
            hprof_name_ = hprof_name;
        }
    }  // namespace leak_monitor
}  // namespace kwai

四.hprof解析性能优化

Shark是如何优化的呢?
Shark是LeakCanary 2.0推出的全新解析组件,其设计思想详见作者的介绍[6],主要做了以下几项优化:

  • 索引,shark低内存开销的最根本原因就是通过索引做到了内存懒加载,遍历hprof时存储对象在hprof中的位置,并为其建立索引方便按需解析。
  • 数据结构上做了深度优化,主要是使用了更高效的map,有2个:第一是对于key和value都是基础类型或字符串的使用hppc做map,第二是对于value不是基本类型的,使用SortedBytesMap存储内容。

具体的索引有:实例索引、类索引、字符串索引、类名索引、数组索引:

/**
 * This class is not thread safe, should be used from a single thread.
 */
internal class HprofInMemoryIndex private constructor(
  private val positionSize: Int,
  private val hprofStringCache: LongObjectScatterMap<String>,
  private val classNames: LongLongScatterMap,
  private val classIndex: SortedBytesMap,
  private val instanceIndex: SortedBytesMap,
  private val objectArrayIndex: SortedBytesMap,
  private val primitiveArrayIndex: SortedBytesMap,
  private val gcRoots: List<GcRoot>,
  private val proguardMapping: ProguardMapping?,
  val primitiveWrapperTypes: Set<Long>
) {

/**
 * Code from com.carrotsearch.hppc.LongLongScatterMap copy pasted, inlined and converted to Kotlin.
 *
 * See https://github.com/carrotsearch/hppc .
 */
class LongLongScatterMap constructor(expectedElements: Int = 4) {

/**
 * A read only map of `id` => `byte array` sorted by id, where `id` is a long if [longIdentifiers]
 * is true and an int otherwise. Each entry has a value byte array of size [bytesPerValue].
 *
 * Instances are created by [UnsortedByteEntries]
 *
 * [get] and [contains] perform a binary search to locate a specific entry by key.
 */
internal class SortedBytesMap(
  private val longIdentifiers: Boolean,
  private val bytesPerValue: Int,
  private val sortedEntries: ByteArray
) {

所谓hppc是High Performance Primitive Collection[7]的缩写,shark使用kotlin将其重写了。hppc只支持基本类型,所以没有了装、拆箱的性能损耗,相关集合操作也做了大量优化,其benchmark可以参考[8]。

经过一番探索与实践,中途还去研究了MAT的源码,我们对其主要做了以下几点优化:

  • GC root剪枝,由于我们搜索Path to GC Root时,是从GC Root自顶向下BFS,如JavaFrame、MonitorUsed等此类GC Root可以直接剪枝。
  • 基本类型、基本类型数组不搜索、不解析。
  • 同类对象超过阈值时不再搜索。
  • 增加预处理,缓存每个类的所有递归super class,减少重复计算。
  • 将object ID的类型从long修改为int,Android虚拟机的object ID大小只有32位,目前shark里使用的都是long来存储的,OOM时百万级对象的情况下,可以节省10M内存。  =》 代码里没看出来

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值