ANR中为什么会出现堆栈漂移

news2024/10/6 2:21:58

前言

我们排查ANR问题的时候,会发现有时候anr文件中捕获的堆栈信息,并不准确,而且经常会打印下面这样的堆栈:

"main" prio=5 tid=1 Native
  | group="main" sCount=1 ucsCount=0 flags=1 obj=0x71ac6f78 self=0xb400007d1e23a7b0
  | sysTid=6066 nice=-10 cgrp=top-app sched=0/0 handle=0x7e6554b4f8
  | state=S schedstat=( 808244194 250487416 988 ) utm=49 stm=31 core=3 HZ=100
  | stack=0x7fd5329000-0x7fd532b000 stackSize=8188KB
  | held mutexes=
  native: #00 pc 00000000000a33b8  /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b)
  native: #01 pc 0000000000010dfc  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+176) (BuildId: 5a0d720732600c94ad8354a1188e9f52)
  native: #02 pc 000000000015a56c  /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44) (BuildId: a31474ac581b716d4588f8c97eb06009)
  at android.os.MessageQueue.nativePollOnce(Native method)
  at android.os.MessageQueue.next(MessageQueue.java:335)
  at android.os.Looper.loopOnce(Looper.java:161)
  at android.os.Looper.loop(Looper.java:288)
  at android.app.ActivityThread.main(ActivityThread.java:7872)
  at java.lang.reflect.Method.invoke(Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

很明显,这种堆栈代表主线程处于空闲状态,既然空闲为什么会ANR呢?这就涉及到本文要讲的,发生了漂移。简单来说,就是捕获的堆栈,并不是发生ANR时的堆栈,因为堆栈已经漂移了。

堆栈漂移介绍

为什么会出现堆栈的漂移呢?这个其实很好理解,系统不可能时时刻刻去观察所有应用的堆栈状态,等到系统意识到某个应用发生ANR的时候,这时候才能开始去采集。但是对于应用侧来说,不可能等到系统采集了之后再继续执行,而且对于APP来说,收到通知之前,也不知道自身发生了ANR了。所以当应用收到系统的通知,开始对堆栈进行采集的时候,往往已经不是发生ANR时的那个堆栈了。

那么,既然会存在这个时间片上的不准确,产生堆栈漂移,那么这个漂移时间是多久呢?知道这一点,对于我们分析ANR是很有帮助的,我们可以反向去查找对应时间点的主线程任务。所以,接下来我们就通过对源码的阅读,一步一步的了解一下,这个漂移是如何产生的,漂移时间是多久。

原理分析

之前讲解ANR流程的时候,说到最终所有的ANR,都会走到ProcessErrorStateRecord的appNotResponding方法(低版本是AppErrors中的appNotResponding方法),典型的功能内聚设计。接下来,我们就简单分析一下这个流程。

本文以android13的源码为例。

class ProcessErrorStateRecord {
    void appNotResponding(){
        ArrayList<Integer> firstPids = new ArrayList<>(5);
        SparseArray<Boolean> lastPids = new SparseArray<>(20);
        ...
        final int pid = mApp.getPid();
        ...
        //这里打印的05-11 16:41:24.073 1141 1156 I am_anr : [0,3416,com.xt.client,550026821,executing service com.xt.client/.TestService]
        EventLog.writeEvent(EventLogTags.AM_ANR, mApp.userId, pid, mApp.processName,mApp.info.flags, annotation);
        //
        firstPids.add(pid);
        if (!isSilentAnr && !onlyDumpSelf) {
            //往firstPids中添加收集清单
        }
        //构建ANR异常描述
        StringBuilder info = new StringBuilder();
        info.setLength(0);
        info.append("ANR in ").append(mApp.processName);
        ...
        //采集堆栈
        File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids, nativePids, tracesFileException, offsets, annotation, criticalEventLog);
        //把ANR异常记录到dropbox中
        mService.addErrorToDropBox()
        //生成
        if (mApp.getWindowProcessController().appNotResponding(info.toString(),...
        if (mService.mUiHandler != null) {
            //延时5S显示ANR弹窗
            mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
        }
    }
}

appNotResponding的整个流程,并且没有什么耗时的点,主要是采集堆栈的dumpStackTraces方法。

public class ActivityManagerService{
    static File dumpStackTraces(...) {
        if (processCpuTracker != null) {
            processCpuTracker.init();
            //首先,sleep200毫秒,用于CPU使用率采集
            Thread.sleep(200);
        }
        processCpuTracker.update();
        //CPU负载最高的5个也加入采集队列
        extraPids = new ArrayList<>();
        for (int i = 0; i < N && extraPids.size() < 5; i++) {
            extraPids.add(stats.pid);
        }
        //生成trace文件
        tracesFile = createAnrDumpFile(tracesDir);
        Pair<Long, Long> offsets = dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
        return tracesFile;
    }
}

可以看到Thread.sleep(200);,线程休眠200毫秒,这是第一个耗时点。

另外system_server对CPU进行分析,以及获取一些必要的状态,也是需要耗费时间的。

接下来我们看一下dumpStackTraces方法,其主要功能就是通过信号量挨个通知被采集的进行已经上报:

public static Pair<Long, Long> dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
        ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
    //捕获最近使用进程的堆栈
    if (firstPids != null) {
        for (int i = 0; i < num; i++) {
            final int pid = firstPids.get(i);
            final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,remainingTime);
        }
    }
    //捕获native进程的堆栈
    if (nativePids != null) {
        for (int pid : nativePids) {
            Debug.dumpNativeBacktraceToFileTimeout(pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
        }
    }
    //捕获高负载应用的堆栈
    if (extraPids != null) {
        for (int pid : extraPids) {
            final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
        }
    }
}

很明显,利用Debug采集其他进程的堆栈信息,也属于一个耗时操作。

总结

所以,我们总结一下,如下流程图所示:

 导致漂移的时间分布主要有如下几点:

1.识别到ANR的时候,系统进程会休眠200毫秒采集CPU信息。

2.系统进程采集一些必要信息,会耗费一些时间,大约100毫秒左右。

3.客户端收到信号去采集ANR的时候,也需要时间,大约要100毫秒左右。

所以总的漂移时间上限应该400到500毫秒左右(当然资源紧张时会有一些偏差)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/622632.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java+mysql校园外卖订餐管理系统servlet

3.1 课题目的 对于客户而言&#xff0c;外卖订单管理系统最好是越方便操作&#xff0c;越简单越好。客户对于外卖订单系统&#xff0c;对他的首要要求就是能够安全&#xff0c;顺利的完成订单。但是很多时候开发人员更在意一些创新的模式&#xff0c;去增加用户的体验感或增值服…

关于变邻域搜索求解柔性作业车间问题的探讨

邻域结构交换内部关键块的操作 譬如&#xff0c;某案例的内部关键块为501—601—502—701&#xff0c;部分OS加工顺序码如下(标注的黄色底纹&#xff1a;为内部关键块) 在移动内部关键快操作时&#xff0c;请教各位是否是如下的变换&#xff1a; ① 块尾701 移至 块内工序50…

vs Git 本地服务器仓库配置

1、安装Git程序 Git 下载地址一https://git-scm.com/download/winGit 下载地址二We bring the awesome Git VCS to Windowshttps://gitforwindows.org/ 2、新建本地服务端仓库 2.1、新建文件夹&#xff0c;并创建仓库 在本地新建文件夹&#xff1a;//本地服务器仓库 在服务…

Vue+SpringBoot打造电子元器件管理系统(附源码文档)

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发、系统定制、远程技术指导。CSDN学院、蓝桥云课认证讲师&#xff0c;全栈领域优质创作者。 一、摘要…

SSRS rdlc报表 七 安装及配置报表服务

开发环境 vs2022 sqlserver 2019 参考文档&#xff1a;安装 SQL Server Reporting Services - SQL Server Reporting Services (SSRS) | Microsoft Learn 配置 URL (Configuration Manager) - SQL Server Reporting Services (SSRS) | Microsoft Learn 如果你在安装数据库的…

基于html+css的图展示114

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

chatgpt赋能python:Python与Kafka的对接方法介绍

Python与Kafka的对接方法介绍 Kafka作为一种高吞吐量的分布式消息系统&#xff0c;近年来在数据处理、大数据分析以及实时流计算等领域得到了广泛的应用&#xff0c;成为了数据处理和分析领域中的标配。而Python作为一种简单易学、功能强大的编程语言&#xff0c;也因其在数据…

学习WooCommerce跨境电商社交媒体营销

WooCommerce 长期以来一直为电子商务店主提供多样化的服务。大约 500 万家商店啓用安装了免费的 WooCommerce 插件。 官方 WooCommerce 插件从 WordPress.org 下载了161,908,802次&#xff0c;并且还在增加。 超过5,106,506 个网站正在使用 WooCommerce。 本文网址: https…

pytorch相关报错【报错】

AttributeError: cannot assign module before Module.init() call 原因&#xff1a;自定义类中缺少supre()函数 解决 KeyError: tensor(1) 报错原因&#xff1a;tensor不能作为字典的键 解决&#xff1a;转化为numpy x torch.tensor([1,2,3]) y {1:1,2:2,3:3} z [y[i]…

提交arxiv的格式调整与流程

提交arxiv的格式调整与流程 进入arxiv网站&#xff08;注册或登录&#xff09;: https://arxiv.org/ 点击发布submission 上传本地资源&#xff0c;完成以下步骤&#xff1a; 许可证一般需要选&#xff1a; 这个表示最小的利益冲突&#xff0c;因为我们最后肯定希望publi…

如何在项目中优雅的抛出异常,这些知识佬们还记得嘛

前言&#xff1a; 本篇文章主要讲解Java中的几种异常关键字和异常类相关知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出&#xff0c;对大佬有帮助希望可以支持下哦~ 小威在此先感谢各位小伙伴儿了&#x1f601; 以…

lua编程——air724二次开发之点亮led教程

一.硬件 硬件型号&#xff1a;银尔达 Core-Air724模块&#xff1b; 杜邦线若干&#xff1b; micro Usb线&#xff1b; USB转TTL工具&#xff1b; 杜邦线用于电源(12V/1A)和TTL信号连接&#xff0c;micro Usb用于下载和调试。 硬件资源&#xff0c;如下图&#xff1a; 二.软件 u…

❤️最详细的Python学习路线-图文不废话

当谈论学习Python编程时&#xff0c;有许多不同的途径和资源可供选择。为了帮助你建立起一个学习Python的有效路线&#xff0c;下面是一个包含基本步骤和关键资源的建议。请注意&#xff0c;这只是一个指南&#xff0c;你可以根据自己的兴趣和学习风格进行调整。 ❝ 以下时间不…

推荐对象转换解决方案:MapStruct

1、什么是MapStruct 1.1 JavaBean 的困扰 对于代码中 JavaBean之间的转换&#xff0c; 一直是困扰我很久的事情。在开发的时候我看到业务代码之间有很多的 JavaBean 之间的相互转化&#xff0c; 非常的影响观感&#xff0c;却又不得不存在。我后来想的一个办法就是通过反射&a…

Django新手必看:从入门到精通Web应用开发①【文末送书三本】

Django新手必看&#xff1a;从入门到精通Web应用开发① 1. Django是什么1.2 Django的由来1.3 Django的命名1.4 Django的版本发布1.5 Django框架的特点 2 Django的设计模式2.1 MVC设计模式2.2 MTV设计模式 3 Django安装与配置3.1 Python支持版本&#xff1a;3.2 Django 3.2与4.1…

Filter设计模式,责任链模式一如既往的妙

场景1&#xff1a; 每种Filter进行某种处理&#xff0c;将各个Filter连在一起形成一个FilterChain。当需要往FilterChain中加入新的Filter&#xff0c;或者改变各个Filter的处理顺序时&#xff0c;可以方便的扩展。 1个接口&#xff1a; public interface Filter {public Str…

前端技术搭建拼图小游戏(内含源码)

The sand accumulates to form a pagoda ✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分 ✨ 写在前面 上周我们实通过前端基础实现了俄罗斯方块游戏&#xff0c;今天还是继续按照我们原定的节奏来带领大家完成一个拼图游戏&#xff0c;功能也比较简单简单&#xff…

chatgpt赋能python:Python数组:如何使用Python将数据存入数组中

Python数组&#xff1a;如何使用Python将数据存入数组中 Python是一个非常强大的高级编程语言&#xff0c;在人工智能、数据科学和机器学习等领域有广泛应用。Python允许将数据存储在不同的数据结构中&#xff0c;其中&#xff0c;数组是最常用的。 在这篇文章中&#xff0c;…

卡尔曼滤波与组合导航原理(一)滤波的基本概念、递推最小二乘

文章目录 一、滤波的基本概念1、传统数字滤波器2、现代控制中的状态观测器3、最优估计的含义4、温度估计的例子1.问题描述2.分析 二、递推最小二乘 课程链接&#xff1a;https://www.bilibili.com/video/BV11K411J7gp/?p1 参考书目&#xff1a;《捷联惯导算法与组合导航原理》…

微信小程序nodejs+vue+uniapp大学体育场馆场地预约系统

开发语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 本文以nodejs为开发技术&#xff0c;一开始&#xff0c;本文就对系统内谈到的基本知识&#xff0c;从整体上进行了描述&#xff0c…