深入浅出分析内存与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问题(提高计算效率),最后优化锁竞争(减少等待)

相关推荐
_一条咸鱼_3 小时前
大厂Android面试秘籍:Activity 结果回调处理(八)
android·面试·android jetpack
_一条咸鱼_3 小时前
大厂Android面试秘籍:Activity 与 Fragment 交互(九)
android·面试·android jetpack
青山渺渺5 小时前
简单记录一下Android四大组件
android
每次的天空6 小时前
Android学习总结之OKHttp拦截器和缓存
android·学习·okhttp
aaajj7 小时前
【Android】ContentResolver的使用
android
时光少年7 小时前
Android ExoPlayer版本升级遇上系统的”瓜“
android·前端
你说你说你来说9 小时前
安卓布局详解
android·笔记
奔跑吧 android9 小时前
【android bluetooth 框架分析 02】【Module详解 3】【HciHal 模块介绍】
android·bluetooth·bt·gd·aosp13·hcihal
好学人10 小时前
Activity的四种启动模型
android
好学人10 小时前
一文了解 Android MVI 架构
android