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

相关推荐
松仔log6 小时前
Java中级——组合和继承
android·java·开发语言
我命由我123459 小时前
Jetpack Compose - MaterialExpressiveTheme 与 MaterialTheme、ColorScheme
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime
lilian23310 小时前
Harmony os 技术实战|拼豆制图06:收藏 ID、生成记录与重启恢复怎么不打架
android·java·数据库·harmonyos
2601_9557596210 小时前
ClaudeAPI成本中心与业务标签设计指南
android·java·数据库
余衫马10 小时前
2026最新版!Android Studio 极速安装与配置指南
android·ide·android studio
AFinalStone10 小时前
Android 7系统异常问题排查(五)Framework层(下)—System Server崩溃
android·tombstone·系统异常
AFinalStone10 小时前
Android 7系统异常问题排查(八)系统追踪—Trace机制与性能诊断
android·系统异常
极客猴子14 小时前
Android录音转写频繁卡顿?多款APP长时间会议场景稳定性实测
android·人工智能·智能手机·飞书
Kapaseker14 小时前
Boolean 变量到底该怎么命名?
android·kotlin
AFinalStone14 小时前
Android 7系统异常问题排查(六)应用异常(上)—ANR机制全解
android·系统异常