深入浅出分析内存与CPU不足导致的ANR问题

深入浅出分析内存与CPU不足导致的ANR问题

一、内存不足如何引发ANR?

1. 内存不足的表现

  • 频繁GC(垃圾回收):主线程被频繁打断
  • OOM前兆:内存抖动导致界面卡顿
  • 低内存杀进程:后台进程被回收后需要冷启动

2. 内存问题在traces中的特征

log 复制代码
"main" prio=5 tid=1 Runnable
  at android.os.MessageQueue.nativePollOnce(Native Method)
  at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:250)
  at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:233)
  
"FinalizerDaemon" prio=5 tid=3 Waiting
  at java.lang.Object.wait(Native Method)
  at java.lang.Object.wait(Object.java:442)
  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:190)

关键特征

  • 出现大量FinalizerDaemon线程活动
  • 主线程频繁进入Native状态(GC导致)

3. 典型案例分析

场景:图片加载未优化

java 复制代码
// 错误示例:每次滑动都创建新Bitmap
recyclerView.addOnScrollListener(new OnScrollListener() {
    @Override
    public void onScrolled() {
        Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/large_image.jpg");
        imageView.setImageBitmap(bitmap); // 未复用旧Bitmap
    }
});

traces表现

  • 主线程频繁执行decodeFile
  • 伴随GC_FOR_ALLOC日志
  • 内存曲线呈锯齿状(内存抖动)

二、CPU不足如何引发ANR?

1. CPU不足的常见原因

原因 影响
过热降频 CPU性能骤降
后台高负载进程 抢占计算资源
死循环/过度计算 单核满载

2. CPU问题在traces中的特征

log 复制代码
"main" prio=5 tid=1 Runnable
  at com.example.app.MainActivity$1.run(MainActivity.java:67)  // 计算密集型代码
  at android.os.Handler.handleCallback(Handler.java:751)
  
"RenderThread" prio=5 tid=4 Waiting
  at java.lang.Object.wait(Native Method)  // 渲染线程等待

关键特征

  • 主线程长时间处于Runnable状态
  • 系统关键线程(如RenderThread)处于等待
  • CPU使用率100%的核与主线程绑定的核一致

3. 典型案例分析

场景:主线程复杂计算

java 复制代码
// 错误示例:主线程解析复杂JSON
button.setOnClickListener(v -> {
    String json = loadHugeJsonFromAsset(); // 10MB JSON
    Data data = new Gson().fromJson(json, Data.class); // 同步解析
});

traces表现

  • 主线程卡在JSON解析方法
  • 无锁竞争和IO等待
  • 对应时间段CPU监控显示单核满载

三、综合问题分析流程

1. 诊断内存问题

graph TD A[查看traces] --> B{发现频繁GC?} B -->|是| C[检查内存分配] B -->|否| D[排除内存因素] C --> E[分析MAT报告] E --> F[定位内存泄漏/抖动]

工具使用

bash 复制代码
# 监控内存分配
adb shell dumpsys meminfo <package>

# 生成HPROF文件
adb shell am dumpheap <package> /data/local/tmp/heap.hprof
adb pull /data/local/tmp/heap.hprof .

2. 诊断CPU问题

graph TD A[查看traces] --> B{主线程Runnable时间长?} B -->|是| C[检查CPU频率] B -->|否| D[排除CPU因素] C --> E[使用systrace] E --> F[定位计算热点]

工具使用

bash 复制代码
# 监控CPU频率
adb shell cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq

# 生成systrace
python systrace.py cpu freq -o trace.html

四、优化方案对比

内存优化方案

问题类型 解决方案 效果
内存泄漏 LeakCanary检测 + 弱引用 减少OOM
内存抖动 对象池 + 缓存控制 平滑GC曲线
大内存分配 Bitmap优化 + 分页加载 降低峰值内存

CPU优化方案

问题类型 解决方案 效果
计算密集型 切线程 + 算法优化 降低主线程负载
过热降频 任务调度策略调整 维持稳定性能
线程竞争 锁优化 + 并发控制 提高多核利用率

五、经典复合问题案例

案例:列表滑动卡顿ANR

现象

  • 快速滑动RecyclerView时触发ANR
  • traces显示主线程阻塞
  • 同时伴随内存警告日志

分析

  1. 内存角度

    • 发现每次滑动都加载新图片
    • 内存抖动导致频繁GC
  2. CPU角度

    • 图片解码占用大量CPU
    • 主线程解码导致渲染延迟

解决方案

java 复制代码
// 使用Glide优化图片加载
Glide.with(context)
    .load(url)
    .override(targetWidth, targetHeight)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(imageView);

// 增加滑动暂停加载
recyclerView.addOnScrollListener(new OnScrollListener() {
    @Override
    public void onScrollStateChanged(int state) {
        if (state == SCROLL_STATE_DRAGGING) {
            Glide.with(context).pauseRequests();
        } else {
            Glide.with(context).resumeRequests();
        }
    }
});

六、预防性监控体系建设

1. 内存监控指标

java 复制代码
// 获取内存状态
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
((ActivityManager)getSystemService(ACTIVITY_SERVICE)).getMemoryInfo(memInfo);
boolean isLowMemory = memInfo.lowMemory;

// 监控关键值
Debug.getNativeHeapAllocatedSize() // Native内存
Runtime.getRuntime().totalMemory() // Java堆内存

2. CPU监控指标

java 复制代码
// 获取CPU使用率
/proc/stat
/proc/<pid>/stat

// 监控关键值
Thread.getThreadCpuTime(threadId) // 线程CPU耗时
SystemClock.currentThreadTimeMillis() // 线程执行时间

3. 线上监控架构

graph LR A[客户端] --> B{资源监控} B -->|内存超标| C[降级策略] B -->|CPU过热| D[限流策略] B -->|正常| E[持续运行] C --> F[上报日志] D --> F F --> G[大数据分析]

七、总结与应对策略

内存问题应对

  1. 预防:定期内存检测 + LeakCanary
  2. 监控:线上OOM率 + 内存曲线
  3. 优化:图片加载 + 数据结构选择

CPU问题应对

  1. 预防:性能测试 + 发热监控
  2. 监控:CPU使用率 + 线程耗时
  3. 优化:算法优化 + 异步拆分

复合问题黄金法则

先解决内存问题(减少GC),再解决CPU问题(提高计算效率),最后优化锁竞争(减少等待)

相关推荐
鸿蒙布道师2 小时前
鸿蒙NEXT开发动画案例5
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
橙子199110167 小时前
在 Kotlin 中什么是委托属性,简要说说其使用场景和原理
android·开发语言·kotlin
androidwork7 小时前
Kotlin Android LeakCanary内存泄漏检测实战
android·开发语言·kotlin
笨鸭先游8 小时前
Android Studio的jks文件
android·ide·android studio
gys98958 小时前
android studio开发aar插件,并用uniapp开发APP使用这个aar
android·uni-app·android studio
H309198 小时前
vue3+dhtmlx-gantt实现甘特图展示
android·javascript·甘特图
像风一样自由8 小时前
【001】renPy android端启动流程分析
android·gitee
千里马学框架10 小时前
重学安卓14/15自由窗口freeform企业实战bug-学员作业
android·framework·bug·systrace·安卓framework开发·安卓窗口系统·自由窗口
xianrenli3816 小时前
android特许权限调试
android
*拯18 小时前
Uniapp Android/IOS 获取手机通讯录
android·ios·uni-app