内存分析工具
1. 内存问题分析原则
a. 对比分析
除非有明确的问题指向,内存问题很难明确使用量是否合理,所以一般和对比机(如升级前版本、竞品机)进行对比分析,找出使用量的差异点,然后针对差异进行分析。
b. 分别定位
系统dumpsys 的meminfo信息有限,需要先meminfo中定位出内存使用异常的种类(heap/mmap/mtrack),然后对不同种类使用不同的分析方法。
2.内存获取与拆分:
通畅我们获取的内存信息来自meminfo,但是这部分只能看到内存的大小,无法确定内存增多或者减少的原因。因此需要按照内存不同种类采用不同方法进行分析。
通常将内存信息分为4个部分来看,分别是native heap、dalvik heap、mmap、GL**/EGL mtrack**。这4个部分内存分别对应不同的分析工具,后面详细介绍。

2.1 Java heap
Java heap对应Dalvik Heap中的Private Dirty与art mmap中Private Dirty +Clean的和。Dalvik Heap使用对应工具分析,art mmap通过smaps分析。
/**
735 * Pss of Java Heap bytes in KB due to the application.
736 * Notes:
737 * * OTHER_ART is the boot image. Anything private here is blamed on
738 * the application, not the system.
739 * * dalvikPrivateDirty includes private zygote, which means the
740 * application dirtied something allocated by the zygote. We blame
741 * the application for that memory, not the system.
742 * * Does not include OTHER_DALVIK_OTHER, which is considered VM
743 * Overhead and lumped into Private Other.
744 * * We don't include dalvikPrivateClean, because there should be no
745 * such thing as private clean for the Java Heap.
746 * @hide
747 */
748 @UnsupportedAppUsage
749 public int getSummaryJavaHeap() {
750 return dalvikPrivateDirty + getOtherPrivate(OTHER_ART);
751 }
/************************************************************************************/
574 case OTHER_ART: return ".art mmap";
2.2 Native heap
Native heap对应Native heap中的Private Dirty(不包含SwapPss Dirty),异常时拆解到Native heap内存分析。
753 /**
754 * Pss of Native Heap bytes in KB due to the application.
755 * Notes:
756 * * Includes private dirty malloc space.
757 * * We don't include nativePrivateClean, because there should be no
758 * such thing as private clean for the Native Heap.
759 * @hide
760 */
761 @UnsupportedAppUsage
762 public int getSummaryNativeHeap() {
763 return nativePrivateDirty;
764 }
2.3 Code
code对应mmap各个值的总和(不包含other mmap),code异常时拆解到mmap内存分析。
766 /**
767 * Pss of code and other static resource bytes in KB due to
768 * the application.
769 * @hide
770 */
771 @UnsupportedAppUsage
772 public int getSummaryCode() {
773 return getOtherPrivate(OTHER_SO)
774 + getOtherPrivate(OTHER_JAR)
775 + getOtherPrivate(OTHER_APK)
776 + getOtherPrivate(OTHER_TTF)
777 + getOtherPrivate(OTHER_DEX)
778 + getOtherPrivate(OTHER_OAT)
779 + getOtherPrivate(OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE)
780 + getOtherPrivate(OTHER_DALVIK_OTHER_APP_CODE_CACHE);
781 }
/************************************************************************************/
568 case OTHER_SO: return ".so mmap";
569 case OTHER_JAR: return ".jar mmap";
570 case OTHER_APK: return ".apk mmap";
571 case OTHER_TTF: return ".ttf mmap";
572 case OTHER_DEX: return ".dex mmap";
573 case OTHER_OAT: return ".oat mmap";
585 case OTHER_DALVIK_OTHER_ZYGOTE_CODE_CACHE: return ".ZygoteJIT";
586 case OTHER_DALVIK_OTHER_APP_CODE_CACHE: return ".AppJIT";
2.4 Stack
Stack对应Stack中Pss Total.
783 /**
784 * Pss in KB of the stack due to the application.
785 * Notes:
786 * * Includes private dirty stack, which includes both Java and Native
787 * stack.
788 * * Does not include private clean stack, because there should be no
789 * such thing as private clean for the stack.
790 * @hide
791 */
792 @UnsupportedAppUsage
793 public int getSummaryStack() {
794 return getOtherPrivateDirty(OTHER_STACK);
795 }
/************************************************************************************/
563 case OTHER_STACK: return "Stack";
2.5 Graphics
Graphics对应EGL GL Gfx(MTK机型不含Gfx)三个值的总和,Graphics异常时拆解到GL/EGL/Gfx内存分析。
797 /**
798 * Pss in KB of graphics due to the application.
799 * Notes:
800 * * Includes private Gfx, EGL, and GL.
801 * * Warning: These numbers can be misreported by the graphics drivers.
802 * * We don't include shared graphics. It may make sense to, because
803 * shared graphics are likely buffers due to the application
804 * anyway, but it's simpler to implement to just group all shared
805 * memory into the System category.
806 * @hide
807 */
808 @UnsupportedAppUsage
809 public int getSummaryGraphics() {
810 return getOtherPrivate(OTHER_GL_DEV)
811 + getOtherPrivate(OTHER_GRAPHICS)
812 + getOtherPrivate(OTHER_GL);
813 }
/************************************************************************************/
566 case OTHER_GL_DEV: return "Gfx dev";
576 case OTHER_GRAPHICS: return "EGL mtrack";
577 case OTHER_GL: return "GL mtrack"

2.6 System
其为 共享内存 + SwapPss。SwapPss只是系统将暂时未用到的内存回收至swap区,依然需要从heap、mmap、EGL/GL mtrack入手进行拆解。
共享内存是对某块内存进行共享使用,现未发现有较大占用的情况,若需要计算每块共享内存,可使用每一项的Pss Total - Private Dirty - Private Clean
/**
832 * Pss in KB due to the system.
833 * Notes:
834 * * Includes all shared memory.
835 * @hide
836 */
837 @UnsupportedAppUsage
838 public int getSummarySystem() {
839 return getTotalPss()
840 - getTotalPrivateClean()
841 - getTotalPrivateDirty();
842 }3. Native heap 分析工具:
3.1 perfetto(通用工具)
https://perfetto.dev/docs/case-studies/memory#heapprofd
Analyzing the Native Heap:
注意使用 debug包,例如分析system_server进程:
$ ./heap_profile -n system_server
Profiling active. Press Ctrl+C to terminate.
You may disconnect your device.
Wrote profiles to /tmp/profile-1283e247-2170-4f92-8181-683763e17445 (symlink /tmp/heap_profile-latest)
These can be viewed using pprof. Googlers: head to pprof/ and upload them.heap_profile_csdn(推荐使用这个)
https://blog.csdn.net/cfc1243570631/article/details/139231897
3.2 Native Heap Insight(推荐使用小米工具)
pre版-debug包
3.3 hprof文件(通用工具)
https://blog.csdn.net/archie_7/article/details/114292255
3.3.1 获取及使用方法:
adb shell am dumpheap 包名
adb pull /data/local/tmp/某.hprof
//使用下列命令进行文件转化
hprof-conv a.hprof b.hprof
拖到Android studio 即可或者在这里打开hprof文件

1、Allocations代表这个class存在几个对象
2、Native Size代表Native内存占用
3、Shallow Size 是指用于存储其本身的内存大小
4、Retained Size 该类关联的所有实例占用内存大小
点击Leaks,Profile会列出当前内存信息中检测到的内存泄露点,如下列出了9点:

3.3.2 官网介绍:
https://developer.android.com/studio/profile/memory-profiler?hl=zh-cn

- 选择要查看的heap的类型:all/app/image/zygote/JNI heaps
-
default heap:当系统未指定堆时。
-
image heap:系统启动映像,包含启动期间预加载的类。此处的分配确保绝不会移动或消失。
-
zygote heap:写时复制堆,其中的应用进程是从 Android 系统中派生的。
-
app heap:您的应用在其中分配内存的主堆。
-
JNI heap:显示 Java 原生接口 (JNI) 引用被分配和释放到什么位置的堆。
- 选择要查看的范围:class、package、callstack
-
Arrange by class:根据类名称对所有分配进行分组。这是默认值。
-
Arrange by package:根据软件包名称对所有分配进行分组。
-
Arrange by callstack:将所有分配分组到其对应的调用堆栈。
-
Allocations:堆中的分配数。
-
Native Size:此对象类型使用的原生内存总量(以字节为单位)。只有在使用 Android 7.0 及更高版本时,才会看到此列。您会在此处看到采用 Java 分配的某些对象的内存,因为 Android 对某些框架类(如 Bitmap)使用原生内存。
-
Shallow Size:此对象类型使用的 Java 内存总量(以字节为单位)。
-
Retained Size:为此类的所有实例而保留的内存总大小(以字节为单位)。
3.4 Malloc debug
Dalvik heap分析工具:
perfetto
https://perfetto.dev/docs/case-studies/memory#java-hprof
Analyzing the Java Heap:
使用方法: ./脚本 -n 进程名
$ ./java_heap_dump -n com.android.systemui
Dumping Java Heap.
Wrote profile to /tmp/tmpup3QrQprofile
This can be viewed using https://ui.perfetto.dev.3.5 mmap分析工具:
smaps获取与解析
使用步骤:
3.5.1. 使用如下命令获取进程PID:
如分析电话进程:
mi@odd:~$ adb shell ps -A |grep com.android.phone
# 输出
radio 2310 843 16412940 124840 do_epoll_wait 0 S com.android.phone3.5.2. 使用如下命令获取进程smaps信息:
如分析电话进程:
adb pull /proc/2310/smaps**注意:**需要抓取多份,以防止内存波动带来的干扰。每次抓取后及时修改文件名称,防止后面数据覆盖前面的数据。
3.5.3. 解析脚本2(推荐):
结合dumpsys meminfo,如果发现3中脚本统计结果和meminfo对不上,可以试下如下脚本:
python3 smaps_parser -f smaps > *.txt脚本下载 http://minio.898311.xyz:8900/blogfile/17298583128804.smaps_parser
3.6 EGL/GL分析工具:
3.7 内存优化方向:
https://zhuanlan.zhihu.com/p/607182345