Skip to content

Latest commit

 

History

History
1092 lines (887 loc) · 31.3 KB

File metadata and controls

1092 lines (887 loc) · 31.3 KB

Android 内存分析结果详解指南

📋 概述

本指南详细解释 Android 内存分析工具生成的解析结果,包括 SMAPS 解析、HPROF 分析和综合分析报告的每一项输出。帮助开发者准确理解分析结果,制定有效的内存优化策略。


🔍 分析工具输出类型

1. SMAPS 解析结果 (smaps_parser.py)

python3 tools/smaps_parser.py -p <pid>
python3 tools/smaps_parser.py -f <smaps_file>

2. HPROF 分析结果 (hprof_parser.py)

python3 tools/hprof_parser.py -f <hprof_file>

3. 全景分析结果 (panorama_analyzer.py)

# 分析 dump 目录
python3 tools/panorama_analyzer.py -d ./dump

# 分析单独文件
python3 tools/panorama_analyzer.py -m meminfo.txt -g gfxinfo.txt

# JSON 输出
python3 tools/panorama_analyzer.py -d ./dump --json -o result.json

4. 使用入口脚本 (analyze.py)

# 一键 Dump 并分析
python3 analyze.py live --package com.example.app

# 分析已有数据
python3 analyze.py panorama -d ./dump

📊 SMAPS 解析结果详解

报告头部信息

========================================
Android App Memory Analysis Report
Generated: 2025-07-12 22:53:18
Script Version: Universal Android Support
========================================

字段解释

  • Generated: 分析报告生成时间

    • 用途: 追踪分析时间点,对比历史数据
    • 重要性: 确保分析数据的时效性
  • Script Version: 脚本版本信息

    • 含义: "Universal Android Support" 表示支持全版本 Android
    • 用途: 确保分析功能的兼容性

内存概览部分

内存概览 / Memory Overview:
总内存使用: 49.70 MB
总交换内存: 0.00 MB

总内存使用 (Total Memory Usage)

  • 数值含义: 49.70 MB

    • 计算方式: 所有内存类型的 PSS 总和
    • 重要性: 🔴 关键指标 - 应用对系统内存的实际贡献
    • 评估标准:
      • 轻量应用: <50MB
      • 普通应用: 50-150MB
      • 重型应用: 150-300MB
      • 异常: >300MB
  • 问题诊断:

    # 对比同类应用内存使用
    # 检查是否超出应用类型的合理范围
    # 分析内存增长趋势
  • 下一步工具:

    • 超出预期: 深入分析各内存类型分布
    • 持续增长: 进行内存泄漏检测

总交换内存 (Total Swap Memory)

  • 数值含义: 0.00 MB

    • 正常情况: Android 通常不使用传统 swap,值为 0
    • 异常情况: 有数值表明内存压力大,系统启用了 swap
    • 性能影响: swap 使用会显著影响性能
  • 问题诊断:

    # 检查系统 swap 配置
    cat /proc/swaps
    
    # 监控 swap 活动
    vmstat 1

详细内存分类

1. Unknown (未知内存类型)

Unknown (未知内存类型) : 0.793 MB
    PSS: 0.793 MB
        [anon:.bss] : 294 kB
        [anon:linker_alloc] : 222 kB
        [anon:thread signal stack] : 176 kB
        [anon:bionic_alloc_small_objects] : 60 kB
        [anon:System property context nodes] : 12 kB
        [anon:bionic_alloc_lob] : 8 kB
        [anon:arc4random data] : 8 kB
        [anon:atexit handlers] : 5 kB
        [anon:cfi shadow] : 4 kB
         : 4 kB
    SwapPSS: 0.000 MB

类型总结行解释

  • 分类名称: "Unknown (未知内存类型)"

    • 含义: 未能明确分类的内存区域
    • 组成: 系统级内存、链接器、信号处理等
  • 总大小: 0.793 MB

    • 计算: 该类型所有内存区域的 PSS 总和
    • 评估: 通常占总内存 2-5%,过高需要分析

PSS 部分详解

  • PSS 值: 0.793 MB
    • 含义: 考虑共享后的实际内存占用
    • 重要性: 用于内存预算计算的关键数据

详细内存条目解释

[anon:.bss] : 294 kB

  • 含义: 程序的 BSS 段(未初始化全局变量)
  • 来源: 编译时分配的全局变量空间
  • 特点: 零初始化数据段
  • 问题诊断: 过大可能有过多全局变量
  • 优化建议: 减少全局变量使用

[anon:linker_alloc] : 222 kB

  • 含义: 动态链接器分配的内存
  • 用途: 动态库加载和符号解析
  • 正常范围: 通常几百KB
  • 问题: 过大可能有库加载问题

[anon:thread signal stack] : 176 kB

  • 含义: 线程信号处理栈
  • 用途: 信号处理时的临时栈空间
  • 数量: 每个线程可能有一个
  • 优化: 检查线程数量是否合理

[anon:bionic_alloc_small_objects] : 60 kB

  • 含义: Bionic C库小对象分配器
  • 用途: 小内存块的高效分配
  • Android特有: Bionic 是 Android 的 C 库
  • 正常: 少量使用是正常的

其他系统内存:

  • System property context nodes (12 kB): 系统属性上下文
  • arc4random data (8 kB): 随机数生成器数据
  • atexit handlers (5 kB): 程序退出处理函数
  • cfi shadow (4 kB): 控制流完整性影子内存

SwapPSS 部分

  • SwapPSS: 0.000 MB
    • 含义: 该类型内存的交换空间使用
    • 正常: Android 通常为 0
    • 异常: 有值表明内存压力

2. Dalvik (Dalvik虚拟机运行时内存)

Dalvik (Dalvik虚拟机运行时内存) : 3.783 MB
    PSS: 3.783 MB
        [anon:dalvik-main space (region space)] : 2500 kB
        [anon:dalvik-zygote space] : 656 kB
        [anon:dalvik-free list large object space] : 327 kB
        [anon:dalvik-non moving space] : 300 kB
    SwapPSS: 0.000 MB

Dalvik 内存分析

总体评估:

  • 总量: 3.783 MB
    • 评估标准:
      • 轻量应用: <20MB
      • 普通应用: 20-80MB
      • 重型应用: 80-200MB
      • 异常: >200MB

主要空间详解:

[anon:dalvik-main space (region space)] : 2500 kB

  • 含义: Dalvik/ART 主要堆空间
  • 用途: Java 对象实例存储
  • 重要性: 🔴 最重要 - 主要的 Java 内存区域
  • 问题诊断:
    • 持续增长: Java 内存泄漏
    • 突然增大: 大对象分配或缓存
  • 下一步工具:
    # HPROF 分析
    python3 hprof_dumper.py -pkg <package>
    python3 hprof_parser.py -f <hprof_file>

[anon:dalvik-zygote space] : 656 kB

  • 含义: Zygote 进程共享的内存空间
  • 用途: 系统类和预加载资源
  • 特点: 多进程共享,节省内存
  • 评估: 相对稳定,缓慢增长正常

[anon:dalvik-free list large object space] : 327 kB

  • 含义: 大对象空间(>12KB 对象)
  • 用途: 存储位图、大数组等
  • 问题: 大对象容易导致内存碎片
  • 优化建议:
    • 避免创建过多大对象
    • 及时释放大位图
    • 使用对象池复用

[anon:dalvik-non moving space] : 300 kB

  • 含义: 不可移动对象空间
  • 用途: Class 对象、JNI 全局引用
  • 特点: GC 时不移动,相对稳定
  • 问题: 异常增长可能是 JNI 引用泄漏

3. Stack (线程栈内存)

Stack (线程栈内存) : 0.976 MB
    PSS: 0.976 MB
        [anon:stack_and_tls:22379] : 100 kB
        [stack] : 60 kB
        [anon:stack_and_tls:25810] : 32 kB
        [anon:stack_and_tls:24523] : 32 kB
        [anon:stack_and_tls:22365] : 32 kB
        [anon:stack_and_tls:22241] : 32 kB
        [anon:stack_and_tls:21976] : 32 kB
        [anon:stack_and_tls:21970] : 32 kB
        [anon:stack_and_tls:21963] : 32 kB
        [anon:stack_and_tls:21962] : 32 kB
    SwapPSS: 0.000 MB

栈内存分析

线程数量统计:

  • 观察: 至少 10 个线程(9个工作线程 + 1个主线程)
  • 计算方式: 每个 stack_and_tls:xxxxx 代表一个线程
  • 评估标准:
    • 正常: 5-20 个线程
    • 较多: 20-50 个线程
    • 过多: >50 个线程

主线程栈: [stack] : 60 kB

  • 含义: 主线程的栈空间
  • 正常大小: 通常 8MB 虚拟空间,实际使用几十 KB
  • 问题: 过大可能有深度递归

工作线程栈: [anon:stack_and_tls:22379] : 100 kB

  • 含义: 工作线程栈 + 线程本地存储
  • 组成: 函数调用栈 + TLS 数据
  • 大小分析:
    • 100 kB: 可能有深度调用或大局部变量
    • 32 kB: 正常线程栈使用

优化建议:

# 检查线程数量
adb shell "ls /proc/<pid>/task | wc -l"

# 分析线程用途
adb shell "cat /proc/<pid>/task/*/comm"

# 线程栈使用监控
watch -n 5 "cat /proc/<pid>/smaps | grep stack | wc -l"

4. 图形设备内存

Gfx dev (图形设备内存) : 5.884 MB
    PSS: 5.884 MB
        /dev/kgsl-3d0 : 5884 kB
    SwapPSS: 0.000 MB

图形内存分析

总量评估:

  • 5.884 MB: 图形内存使用量
  • 评估标准:
    • 轻量应用: <10MB
    • 普通应用: 10-50MB
    • 图形密集: 50-200MB
    • 游戏应用: 可能更高

设备详解: /dev/kgsl-3d0 : 5884 kB

  • 含义: Qualcomm GPU 图形内存设备
  • 用途:
    • OpenGL 纹理和缓冲区
    • 渲染目标和帧缓冲
    • GPU 计算内存
  • 特点:
    • 无法换出到存储
    • 直接占用显存
    • 影响图形性能

问题诊断:

  • 过大原因:
    • 大尺寸纹理未压缩
    • 过多渲染缓冲区
    • 资源未及时释放

下一步工具:

# GPU 内存详情 (如果支持)
adb shell "cat /d/kgsl/proc/<pid>/mem"

# 图形性能分析
adb shell "dumpsys gfxinfo <package> framestats"

# GPU 调试工具
# Mali Graphics Debugger, RenderDoc

5. 共享库内存

.so mmap (动态链接库映射内存) : 2.608 MB
    PSS: 2.608 MB
        /vendor/lib64/libllvm-glnext.so : 859 kB
        /system/lib64/libhwui.so : 804 kB
        /vendor/lib64/egl/libGLESv2_adreno.so : 235 kB
        /system/lib64/libgui.so : 75 kB
        /system/lib64/libsqlite.so : 70 kB
        /system/lib64/libft2.so : 57 kB
        /system/lib64/libandroid_runtime.so : 54 kB
        /system/lib64/libharfbuzz_ng.so : 48 kB
        /system/lib64/libmediandk.so : 36 kB
        /system/lib64/libminikin.so : 29 kB
    SwapPSS: 0.000 MB

共享库分析

总体评估:

  • 2.608 MB: 共享库内存贡献
  • 特点: PSS 值通常远小于实际库大小(因为共享)
  • 效率: 体现了共享内存的优势

主要库解析:

图形相关库:

  • libllvm-glnext.so (859 kB): LLVM GPU 编译器
  • libGLESv2_adreno.so (235 kB): Adreno GPU OpenGL ES 驱动
  • libhwui.so (804 kB): Android 硬件UI渲染库

系统核心库:

  • libgui.so (75 kB): 图形用户界面库
  • libandroid_runtime.so (54 kB): Android 运行时库
  • libsqlite.so (70 kB): SQLite 数据库库

字体和文本:

  • libft2.so (57 kB): FreeType 字体渲染
  • libharfbuzz_ng.so (48 kB): 文本排版引擎
  • libminikin.so (29 kB): Android 文本布局

优化分析:

# 计算共享效率
# 如果单个库实际大小是 5MB,但 PSS 只有 500KB
# 说明该库被 10 个进程共享: 5MB / 10 = 500KB

# 检查库依赖
adb shell "cat /proc/<pid>/maps | grep '\.so' | wc -l"

# 分析库加载时机
# 考虑延迟加载不常用的库

6. Android 应用文件

.apk mmap (APK文件映射内存) : 3.048 MB
    PSS: 3.048 MB
        /system_ext/priv-app/Launcher3QuickStep/Launcher3QuickStep.apk : 2480 kB
        /system/framework/framework-res.apk : 508 kB
        /product/app/QuickSearchBox/QuickSearchBox.apk : 60 kB
    SwapPSS: 0.000 MB

APK 内存分析

主应用 APK: Launcher3QuickStep.apk : 2480 kB

  • 含义: 主应用 APK 的内存映射
  • 包含: DEX 代码、资源文件、Native 库
  • 优化: PSS 相对较低说明有一定共享

系统框架: framework-res.apk : 508 kB

  • 含义: Android 系统资源包
  • 共享: 被所有应用共享
  • 效率: 高度共享降低了单应用成本

问题分析:

  • APK 内存过大:
    • 检查 APK 文件大小
    • 分析资源使用效率
    • 考虑资源压缩和按需加载

7. ART 编译文件

.art mmap (ART运行时文件映射内存) : 2.727 MB
    PSS: 2.727 MB
        [anon:dalvik-/system/framework/boot-framework.art] : 1537 kB
        [anon:dalvik-/apex/com.android.art/javalib/boot.art] : 369 kB
        [anon:dalvik-/data/dalvik-cache/arm64/app.art] : 344 kB
        [anon:dalvik-/system/framework/boot-core-icu4j.art] : 230 kB
        [anon:dalvik-/apex/com.android.art/javalib/boot-core-libart.art] : 97 kB
        [anon:dalvik-/system/framework/boot-telephony-common.art] : 58 kB
        [anon:dalvik-/system/framework/boot-voip-common.art] : 30 kB
        /system/framework/arm64/boot-framework.art : 16 kB
        [anon:dalvik-/apex/com.android.art/javalib/boot-bouncycastle.art] : 10 kB
        [anon:dalvik-/apex/com.android.art/javalib/boot-okhttp.art] : 8 kB
    SwapPSS: 0.000 MB

ART 文件解析

系统框架 ART: boot-framework.art : 1537 kB

  • 含义: Android 框架的 AOT 编译代码
  • 用途: 提高系统启动和运行性能
  • 共享: 系统级共享,多应用受益

应用专用 ART: app.art : 344 kB

  • 含义: 应用代码的 AOT 编译版本
  • 用途: 提高应用启动速度和执行效率
  • 生成: 应用安装或系统空闲时编译

性能影响:

  • 内存换性能: ART 文件占用内存但提升执行效率
  • 启动优化: 预编译代码减少启动时间

8. Native 内存分配器

scudo heap (Scudo安全内存分配器) : 24.482 MB
    PSS: 24.482 MB
        [anon:scudo:primary] : 16491 kB
        [anon:scudo:secondary] : 7991 kB
    SwapPSS: 0.000 MB

Scudo 分配器分析

总体评估:

  • 24.482 MB: Native 内存使用的主要部分
  • 重要性: 🔴 关键 - Native 内存泄漏的主要指标
  • Android 版本: Android 11+ 默认使用

主要池详解: [anon:scudo:primary] : 16491 kB

  • 含义: 主要内存池,用于常见大小的分配
  • 特点:
    • 高效的小到中等大小内存分配
    • 提供安全保护机制
    • 检测内存错误

[anon:scudo:secondary] : 7991 kB

  • 含义: 次要内存池,用于大内存分配
  • 用途: 大于主池分配粒度的内存块
  • 特点: 处理不规则大小的分配

问题诊断:

  • 持续增长: Native 内存泄漏

    # Native 内存详细分析
    adb shell "dumpsys meminfo <package> -d"
    
    # 检查 Native 内存分配
    # 使用 AddressSanitizer 或 malloc hooks
  • 异常大值: 检查 JNI 代码、第三方库

  • 分配模式: 分析 primary vs secondary 比例


🔍 HPROF 分析结果详解

基本文件信息

开始解析HPROF文件: com.tencent.mm_8234_20250112_143022.hprof
HPROF版本: JAVA PROFILE 1.0.3
标识符大小: 4 bytes
时间戳: 2025-01-12 14:30:22

文件元信息

  • HPROF版本: 标准 Java 堆转储格式版本
  • 标识符大小: 对象引用的字节数(32位=4字节,64位=8字节)
  • 时间戳: 堆转储的确切时间

总体统计信息

=== 内存分析完成 ===
总实例数: 2,456,789
实例总大小: 89.34 MB
总数组数: 345,678
数组总大小: 23.45 MB
总内存使用: 112.79 MB

关键指标解释

总实例数: 2,456,789

  • 含义: Java 堆中所有对象实例的总数
  • 评估标准:
    • 轻量应用: <100万
    • 普通应用: 100-500万
    • 重型应用: 500-2000万
    • 异常: >2000万
  • 问题诊断: 过多实例可能表明:
    • 对象创建过于频繁
    • 缺乏对象复用机制
    • 内存泄漏导致对象累积

实例总大小: 89.34 MB

  • 含义: 所有普通对象占用的内存总量
  • 计算: 不包括数组的对象内存总和
  • 占比: 通常占 Java 堆的 60-80%

总数组数: 345,678

  • 含义: 堆中所有数组对象的数量
  • 包括: 基本类型数组和对象数组
  • 关注点: 大数组的内存占用

数组总大小: 23.45 MB

  • 含义: 所有数组对象占用的内存
  • 占比: 通常占 Java 堆的 20-40%
  • 问题: 占比过高可能有大数组泄漏

总内存使用: 112.79 MB

  • 计算: 实例总大小 + 数组总大小
  • 对比: 应与 SMAPS 中 Dalvik 内存相近
  • 差异: HPROF 只统计 Java 对象,SMAPS 包括 JVM 开销

TOP 内存占用类分析

=== TOP 20 内存占用类 (最小 0.1MB) ===
类名                                          实例数      总大小(MB)    平均大小(KB)
--------------------------------------------------------------------------------------
java.lang.String                            234,567     15.67         0.07
android.graphics.Bitmap                     1,234       12.34         10.24
com.tencent.mm.ui.ChatActivity              45          8.91          203.56
byte[]                                       89,012      7.23          0.08
java.util.HashMap$Node                      123,456     6.78          0.06
android.view.View                            45,678      5.43          0.12
com.tencent.mm.model.Message                23,456      4.32          0.19

逐行分析

java.lang.String - 15.67 MB

  • 实例数: 234,567(数量最多)
  • 平均大小: 0.07 KB(非常小)
  • 分析:
    • 字符串对象众多但单个很小
    • 可能有字符串重复问题
    • 考虑字符串常量池优化
  • 优化建议:
    // 使用 StringBuilder 避免频繁字符串拼接
    StringBuilder sb = new StringBuilder();
    
    // 重用字符串常量
    private static final String CONSTANT = "常量字符串";
    
    // 使用 intern() 减少重复字符串
    String optimized = someString.intern();

android.graphics.Bitmap - 12.34 MB

  • 实例数: 1,234(相对较少)
  • 平均大小: 10.24 KB(较大)
  • 分析:
    • 位图是内存使用的重点
    • 单个位图占用较多内存
    • 需要重点优化图片使用
  • 问题诊断:
    # 计算位图总数是否合理
    # 1,234个位图较多,检查是否有泄漏
    # 平均10KB不算太大,但总量需要控制
  • 优化建议:
    // 及时回收位图
    if (bitmap != null && !bitmap.isRecycled()) {
        bitmap.recycle();
    }
    
    // 使用合适的图片格式和尺寸
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 2; // 缩放
    options.inPreferredConfig = Bitmap.Config.RGB_565; // 减少内存

com.tencent.mm.ui.ChatActivity - 8.91 MB

  • 实例数: 45(异常多)
  • 平均大小: 203.56 KB(很大)
  • 问题分析: 🚨 严重问题
    • Activity 实例数过多(应该只有1-2个)
    • 单个 Activity 过大
    • 明显的 Activity 内存泄漏
  • 泄漏原因:
    • Static 引用持有 Activity
    • Handler 未正确清理
    • 监听器未注销
    • 匿名内部类持有外部引用
  • 下一步工具:
    # 使用 LeakCanary 自动检测
    # 使用 MAT (Memory Analyzer Tool) 分析引用链
    # 检查 Activity 的引用路径

byte[] - 7.23 MB

  • 分析: 字节数组,通常用于:
    • 图片数据存储
    • 网络数据缓存
    • 文件读写缓冲区
  • 优化: 检查是否有不必要的字节数组缓存

java.util.HashMap$Node - 6.78 MB

  • 分析: HashMap 内部节点
  • 实例数: 123,456(很多)
  • 问题: 可能有过多的 HashMap 或单个 HashMap 过大
  • 优化: 考虑使用 SparseArray 等更轻量的集合

数组内存统计

=== TOP 10 基本类型数组内存占用 ===
数组类型            数组数量      总大小(MB)    平均大小(KB)
----------------------------------------------------------
byte[]             89,012       7.23          0.08
int[]              12,345       3.45          0.29
char[]             56,789       2.78          0.05
long[]             3,456        1.23          0.37

数组类型分析

byte[] 数组:

  • 数量: 89,012(最多)
  • 总大小: 7.23 MB
  • 平均大小: 0.08 KB(很小)
  • 用途推测:
    • 图片解码缓冲区
    • 网络数据包
    • 字符串的字节表示
  • 优化: 考虑对象池复用小字节数组

int[] 数组:

  • 平均大小: 0.29 KB(相对较大)
  • 可能用途: 像素数据、坐标数组、索引数组

char[] 数组:

  • 与字符串相关: char[]是String内部存储
  • 数量大: 与String数量对应

long[] 数组:

  • 平均最大: 0.37 KB
  • 可能用途: 时间戳数组、ID数组

字符串统计详情

=== 字符串内存统计 ===
字符串实例数: 234,567
字符串总大小: 15.67 MB
平均字符串大小: 70.12 bytes

字符串性能分析

字符串密度: 15.67MB / 112.79MB = 13.9%

  • 评估: 字符串占比适中
  • 优化潜力: 仍有优化空间

平均大小: 70.12 bytes

  • 分析: 中等长度字符串
  • 对比:
    • <50 bytes: 短字符串,考虑常量池
    • 50-200 bytes: 中等长度,正常
    • 200 bytes: 长字符串,检查是否必要

优化策略:

// 1. 字符串常量池
private static final String[] COMMON_STRINGS = {
    "常用字符串1", "常用字符串2"
};

// 2. StringBuilder 复用
private final StringBuilder mStringBuilder = new StringBuilder();

// 3. 字符串缓存
private final LruCache<String, String> mStringCache = 
    new LruCache<>(100);

📈 全景分析结果详解

内存使用总结

══════════════════════════════════════════════════════════════════════════════
                      Android 应用内存全景分析报告
══════════════════════════════════════════════════════════════════════════════
                         com.android.systemui
                         2025-01-15 10:30:22
══════════════════════════════════════════════════════════════════════════════

────────────────────────────────────────
[ 内存概览 ]
────────────────────────────────────────
Total PSS:                   142.08 MB
Java Heap:                   31.85 MB
Native Heap:                 44.76 MB
Graphics:                    29.27 MB
Code:                        15.29 MB
Stack:                        1.11 MB

关键指标解读

Total PSS: 142.08 MB

  • 来源: dumpsys meminfo 的 TOTAL PSS
  • 包含: Java堆 + Native堆 + Graphics + Code + Stack + 其他
  • 评估: 是进程对系统内存贡献的最准确指标

各内存分类占比计算:

  • 计算: 各分类 / Total PSS * 100%
  • 评估标准:
    • Java Heap: 20-40% 正常
    • Native Heap: 20-40% 正常
    • Graphics: 10-30% 正常(图形密集型应用可能更高)
    • Code: 5-15% 正常
  • 分析: 通过占比可以快速判断应用内存使用的特点

内存分布分析

────────────────────────────────────────
[ Native 内存追踪 ]
────────────────────────────────────────
Native Heap PSS:   44.76 MB (100%)
├── 可追踪:        39.00 MB (87.1%)
│   ├── Bitmap:    18.37 MB (41.0%)
│   ├── Malloced:  15.43 MB (34.5%)
│   └── NonMalloced: 5.20 MB (11.6%)
└── 未追踪:         5.76 MB (12.9%)

内存分布健康度

Java Heap: 31.85 MB

  • 评估: 占比合理
  • 关注: 是否有持续增长趋势,使用 HPROF 深入分析

图形内存: 29.27 MB

  • 评估: 图形内存使用
  • 问题: 可能的 UI 过度绘制或纹理泄漏
  • 优化: 检查位图使用和 GPU 资源管理

Native 内存追踪:

  • 可追踪 (87.1%): 在 dumpsys meminfo Native Allocations 中有记录
  • 未追踪 (12.9%): 第三方库、JNI 直接分配等
  • 警告阈值: 未追踪占比 >30% 需要关注

优化建议详解

💡 优化建议:
------------------------------
⚠️ [Java堆内存] Java堆内存使用量较大 (89.3MB),建议检查内存泄漏
ℹ️ [字符串优化] 字符串占用 15.7MB,建议优化字符串使用,考虑使用StringBuilder或字符串常量池
ℹ️ [图形内存] 图形内存使用较高 (45.7MB),检查位图缓存和GPU内存使用
ℹ️ [Native内存] Native堆内存使用较高 (34.2MB),检查JNI代码和第三方库

建议优先级和执行步骤

🔴 高优先级: Java堆内存检查

# 1. 执行内存泄漏检测
python3 hprof_dumper.py -pkg <package> -o before/
# 执行操作
python3 hprof_dumper.py -pkg <package> -o after/
# 对比分析

# 2. 使用自动检测工具
# 集成 LeakCanary
implementation 'com.squareup.leakcanary:leakcanary-android:2.12'

# 3. MAT 工具分析
# 下载 Eclipse Memory Analyzer Tool
# 导入 HPROF 文件分析引用链

🟡 中优先级: 字符串优化

// 1. 字符串常量池使用
private static final String CACHED_STRING = "常用字符串";

// 2. StringBuilder 复用
private final StringBuilder mBuilder = new StringBuilder(256);

// 3. 字符串缓存
private final Map<String, String> mStringCache = new HashMap<>();

🟢 低优先级: 图形内存优化

// 1. 位图回收
if (bitmap != null && !bitmap.isRecycled()) {
    bitmap.recycle();
    bitmap = null;
}

// 2. 图片加载优化
Glide.with(context)
    .load(url)
    .override(targetWidth, targetHeight)
    .format(DecodeFormat.PREFER_RGB_565)
    .into(imageView);

// 3. LRU 缓存
private final LruCache<String, Bitmap> mBitmapCache = 
    new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            return bitmap.getByteCount();
        }
    };

🚨 异常模式识别和处理

1. 内存泄漏模式

Activity 泄漏

com.example.MainActivity: 5 instances, 12.3 MB

特征: Activity 实例数 > 2 原因: Static 引用、Handler、监听器 处理:

// 1. 避免 static 引用 Activity
// 错误
private static Context sContext;

// 正确
private static WeakReference<Context> sContextRef;

// 2. Handler 正确使用
private static class MyHandler extends Handler {
    private final WeakReference<Activity> mActivityRef;
    
    MyHandler(Activity activity) {
        mActivityRef = new WeakReference<>(activity);
    }
}

// 3. 监听器注销
@Override
protected void onDestroy() {
    eventBus.unregister(this);
    super.onDestroy();
}

集合泄漏

java.util.ArrayList: 50,000 instances, 25.6 MB

特征: 集合实例数异常多 原因: 集合未清理、缓存无限增长 处理:

// 1. 使用 WeakHashMap
Map<Key, Value> cache = new WeakHashMap<>();

// 2. LRU 缓存
LruCache<String, Object> cache = new LruCache<>(maxSize);

// 3. 定期清理
if (cache.size() > MAX_SIZE) {
    cache.clear();
}

2. 大对象问题

位图过大

android.graphics.Bitmap: 2 instances, 48.5 MB average: 24.25 MB

特征: 单个对象异常大 处理:

// 1. 图片压缩
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inPreferredConfig = Bitmap.Config.RGB_565;

// 2. 分片加载大图
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(inputStream, false);
Bitmap region = decoder.decodeRegion(rect, options);

字节数组过大

byte[]: 10 instances, 32.1 MB average: 3.21 MB

处理:

// 1. 流式处理
try (InputStream is = new FileInputStream(file);
     OutputStream os = new FileOutputStream(output)) {
    byte[] buffer = new byte[8192]; // 小缓冲区
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
        os.write(buffer, 0, bytesRead);
    }
}

// 2. 对象池复用
public class ByteArrayPool {
    private final Queue<byte[]> pool = new ConcurrentLinkedQueue<>();
    
    public byte[] acquire(int size) {
        byte[] array = pool.poll();
        if (array == null || array.length < size) {
            array = new byte[size];
        }
        return array;
    }
    
    public void release(byte[] array) {
        pool.offer(array);
    }
}

3. 内存碎片问题

特征识别

# 大量小对象
java.lang.Object: 1,000,000 instances, average: 24 bytes

# 内存使用不连续
Virtual: 512 MB, RSS: 128 MB (25% utilization)

解决方案

// 1. 对象池
public class ObjectPool<T> {
    private final Queue<T> pool = new ArrayDeque<>();
    private final Factory<T> factory;
    
    public T acquire() {
        T item = pool.poll();
        return item != null ? item : factory.create();
    }
    
    public void release(T item) {
        reset(item);
        pool.offer(item);
    }
}

// 2. 内存预分配
List<Object> preAllocated = new ArrayList<>(expectedSize);

// 3. 减少临时对象
// 错误:频繁创建对象
for (int i = 0; i < 1000; i++) {
    String temp = "prefix" + i + "suffix";
}

// 正确:复用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.setLength(0);
    sb.append("prefix").append(i).append("suffix");
    String result = sb.toString();
}

🔗 相关工具和资源链接

基础分析工具

高级分析工具

Java 堆分析

Native 内存分析

Android 专用工具

本项目工具

  • analyze.py: 入口脚本,支持 live dump 和 panorama 分析
  • panorama_analyzer.py: 全景分析核心
  • hprof_parser.py: HPROF 文件解析
  • smaps_parser.py: SMAPS 文件解析
  • meminfo_parser.py: dumpsys meminfo 解析
  • gfxinfo_parser.py: dumpsys gfxinfo 解析
  • zram_parser.py: zRAM/Swap 分析
  • diff_analyzer.py: 对比分析

在线资源


💡 总结和最佳实践

本指南聚焦于分析结果的解读与诊断逻辑。

关于团队教学流程、报告模板和 Android 16 兼容检查,请统一使用:

推荐配合方式:

  1. 本文用于读懂输出、定位问题。
  2. teaching_playbook.md 用于培训执行流程、监控脚本基线和评审检查项。
  3. 基线与复验必须使用同一采集策略,避免口径漂移。