克魔助手 - iOS性能检测平台

前言

众所周知,如今的用户变得越来越关心app的体验,开发者必须关注应用性能所带来的用户流失问题。目前危害较大的性能问题主要有:闪退、卡顿、发热、耗电快、网络劫持等,但是做过iOS开发的人都知道,在开发过程中我们没有一个很直观的工具可以实时的知道开发者写出来的代码会不会造成性能问题,虽然Xcode里提供了耗电量检测、内存泄漏检测等工具,但是这些工具使用效果并不理想(如Leak无法发现循环引用造成的内存泄漏)。所以这篇文章主要是介绍一款实时监控app各项性能指标的工具,包括CPU占用率、内存使用量、内存泄漏、FPS、卡顿检测,并且会分析造成这些性能问题的原因。

CPU

CPU 是移动设备最重要的组成部分,如果开发者写的代码有问题导致CPU负载过高,会导致app使用过程中发生卡顿,同时也可能导致手机发热发烫,耗电过快,严重影响用户体验。 如果想避免CPU负载过高可以通过检测app的CPU使用率,然后可以发现导致CPU过高的代码,并根据具体情况优化。那该如何检测CPU使用率呢?大学期间学过计算机的应该都上过操作系统这门课,学过的都知道线程CPU是调度和分配的基本单位,而应用作为进程运行时,包含了多个不同的线程,这样如果我们能知道app里所有线程占用 CPU 的情况,也就能知道整个app的 CPU 占用率。幸运的是我们在Mach 层中 thread_basic_info 结构体发现了我们想要的东西,thread_basic_info 结构体定义如下:

CPU内存监控

克魔助手提供了分析内存占用、查看 CPU 实时活动数据以及追踪特定应用程序的功能,让开发者可以更好地了解应用程序的运行情况。 以下是一些示例截图:





 





 





同样,克魔助手还提供了内存、GPU 性能监控、网络监控等功能,开发者可以查看实时数据活动和追踪应用程序的特定功能。 如下:

内存监控

以下是内存监控的示例截图:

Memory

物理内存(RAM)与 CPU 一样都是系统中最稀少的资源,也是最有可能产生竞争的资源,应用内存与性能直接相关 - 通常是以牺牲别的应用为代价。 不像 PC 端,iOS 没有交换空间作为备选资源,这就使得内存资源尤为重要。

App占用的内存

获取app内存的API同样可以在Mach层找到,mach_task_basic_info 结构体存储了 Mach task 的内存使用信息,其中 resident_size 就是应用使用的物理内存大小,virtual_size 是虚拟内存大小。

#define MACH_TASK_BASIC_INFO     20         /* always 64-bit basic info */
struct mach_task_basic_info {mach_vm_size_t  virtual_size;       /* virtual memory size (bytes) */mach_vm_size_t  resident_size;      /* resident memory size (bytes) */mach_vm_size_t  resident_size_max;  /* maximum resident memory size (bytes) */time_value_t    user_time;          /* total user run time forterminated threads */time_value_t    system_time;        /* total system run time forterminated threads */policy_t        policy;             /* default policy for new threads */integer_t       suspend_count;      /* suspend count for task */
};

最后得到获取当前 App Memory 的使用情况:

- (CGFloat)usedMemory {task_basic_info_data_t taskInfo;mach_msg_type_number_t infoCount = TASK_BASIC_INFO_COUNT;kern_return_t kernReturn = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&taskInfo, &infoCount);if (kernReturn != KERN_SUCCESS) {return NSNotFound;}CGFloat value = (CGFloat)(taskInfo.resident_size / 1024.0 / 1024.0);return value;
}

设备已使用的内存

+ (CGFloat)deviceUsedMemory {size_t length = 0;int mib[6] = {0};int pagesize = 0;mib[0] = CTL_HW;mib[1] = HW_PAGESIZE;length = sizeof(pagesize);if (sysctl(mib, 2, &pagesize, &length, NULL, 0) < 0) {return 0;}mach_msg_type_number_t count = HOST_VM_INFO_COUNT;vm_statistics_data_t vmstat;if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count) != KERN_SUCCESS) {return 0;}int wireMem = vmstat.wire_count * pagesize;int activeMem = vmstat.active_count * pagesize;return (CGFloat)(wireMem + activeMem) / 1024.0 / 1024.0;
}

##设备可用的内存

+ (CGFloat)deviceAvailableMemory {vm_statistics64_data_t vmStats;mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT;kern_return_t kernReturn = host_statistics(mach_host_self(),HOST_VM_INFO,(host_info_t)&vmStats,&infoCount);if (kernReturn != KERN_SUCCESS) {return NSNotFound;}return (CGFloat)(vm_page_size * (vmStats.free_count + vmStats.inactive_count)  / 1024.0 / 1024.0);
}

FPS

FPS即屏幕每秒的刷新率,范围在0-60之间,60最佳。FPS是测量用于保存、显示动态视频的信息数量,每秒钟帧数愈多,所显示的动作就会愈流畅,优秀的app都要保证FPS 在 55-60 之间,这样才会给用户流畅的感觉,反之,用户则会感觉到卡顿。 对于FPS的计算网上争议颇多,这边使用的和 YYKit 中的 YYFPSLabel 原理一样,系统提供了 CADisplayLink 这个 API,该API在屏幕每次绘制的时候都会回调,通过接收 CADisplayLink 的回调,计算每秒钟收到的回调次数得到屏幕每秒的刷新次数,从而得到 FPS,具体代码如下:

首先,为了使用克魔助手检查iOS游戏的帧率,需要在电脑上安装爱思助手或者iTunes驱动。随后,通过USB连接苹果手机到电脑,并启动克魔助手。




 

设备选择

第二步,在工具左侧菜单栏中,打开“设备”窗口。窗口中会显示你连接的所有苹果设备,选择要查询的设备,然后选中设备。




 

查看FPS和fps监测

第三步,点击“开始监听”,在右侧的“FPS曲线”查看当前应用程序的帧率。另外,在克魔助手中可以看到每秒图像

更新多少次,以及总图像更新次数,从而了解游戏性能如何。





 




 

CPU占比和应用追踪及特定APP数据

第四步,查看帧率是保证游戏顺畅性的重要因素,所以应用程序开发人员将会经常使用克魔助手检查苹果手机玩游戏的帧率。当然,游戏还有很多需要优化的地方,可以提高游戏整体性能,所以克魔助手不仅提供了监控帧率,还提供了可以分析内存占用,查看CPU实时活动数据,以及追踪特定app时等功能,让开发者可以更好地了解游戏运行情况。




 




 




 

通过克魔助手的帮助,开发者可以方便快捷地检查苹果手机玩游戏的帧率。了解帧率和游戏性能对增加游戏用户量,增强用户体验和游戏的质量都非常重要,所以在开发过程中不要忘记检查苹果手机的帧率。

值得注意的是基于 CADisplayLink 实现的 FPS 在生产场景中只有指导意义,不能代表真实的 FPS,因为基于 CADisplayLink 实现的 FPS 无法完全检测出当前 Core Animation 的性能情况,它只能检测出当前 RunLoop 的帧率。

Freezing

为什么会出现卡顿

从一个像素到最后真正显示在屏幕上,iPhone 究竟在这个过程中做了些什么?想要了解背后的运作流程,首先需要了解屏幕显示的原理。iOS 上完成图形的显示实际上是 CPU、GPU 和显示器协同工作的结果,具体来说,CPU 负责计算显示内容,包括视图的创建、布局计算、图片解码、文本绘制等,CPU 完成计算后会将计算内容提交给 GPU,GPU 进行变换、合成、渲染后将渲染结果提交到帧缓冲区,当下一次垂直同步信号(简称 V-Sync)到来时,最后显示到屏幕上

上文中提到 V-Sync 是什么,以及为什么要在 iPhone 的显示流程引入它呢?在 iPhone 中使用的是双缓冲机制,即上图中的 FrameBuffer 有两个缓冲区,双缓冲区的引入是为了提升显示效率,但是与此同时,他引入了一个新的问题,当视频控制器还未读取完成时,比如屏幕内容刚显示一半时,GPU 将新的一帧内容提交到帧缓冲区并把两个缓冲区进行交换后,视频控制器就会把新的一帧数据的下半段显示到屏幕上,造成画面撕裂现象,V-Sync 就是为了解决画面撕裂问题,开启 V-Sync 后,GPU 会在显示器发出 V-Sync 信号后,去进行新帧的渲染和缓冲区的更新。

搞清楚了 iPhone 的屏幕显示原理后,下面来看看在 iPhone 上为什么会出现卡顿现象,上文已经提及在图像真正在屏幕显示之前,CPU 和 GPU 需要完成自身的任务,而如果他们完成的时间错过了下一次 V-Sync 的到来(通常是1000/60=16.67ms),这样就会出现显示屏还是之前帧的内容,这就是界面卡顿的原因(离屏渲染就是典型的卡顿问题)。不难发现,无论是 CPU 还是 GPU 引起错过 V-Sync 信号,都会造成界面卡顿。

那如何检测卡顿呢?比较常见的思路是:开辟一条单独的子线程,让这条子线程去实时检测主线程的 RunLoop 情况,实时计算 kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting 两个状态之间的耗时是否超过某个阀值,如果超过阈值即认定主线程发生了卡顿。下面是代码实现:

static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{MyClass *object = (__bridge MyClass*)info;// 记录状态值object->activity = activity;// 发送信号dispatch_semaphore_t semaphore = moniotr->semaphore;dispatch_semaphore_signal(semaphore);
}- (void)registerObserver
{CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities,YES,0,&runLoopObserverCallBack,&context);CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);// 创建信号semaphore = dispatch_semaphore_create(0);// 在子线程监控时长dispatch_async(dispatch_get_global_queue(0, 0), ^{while (YES){// 假定连续5次超时50ms认为卡顿(当然也包含了单次超时250ms)long st = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 50*NSEC_PER_MSEC));if (st != 0){if (activity==kCFRunLoopBeforeSources || activity==kCFRunLoopAfterWaiting){if (++timeoutCount < 5)continue;// 检测到卡顿,进行卡顿上报}}timeoutCount = 0;}});
}              

当检测到卡顿后可以进一步收集卡顿现场,如堆栈信息等,关于收集堆栈信息这里就不细说,很多第三方库都有实现,我之前是使用了项目中已经集成的收集崩溃信息的三方库,通过这个库在收集堆栈信息。

MemoryLeak

内存泄漏也是造成app内存过高的主要原因,如果iPhone手机的性能都很强,如果一个app会因为内存过高被系统强制杀掉,大部分都是存在内存泄漏。内存泄漏对于开发和测试而言表现得并不明显,如果它不泄漏到一定程度是用户是无法察觉的,但是这也是开发者必须杜绝的一大问题。

查找内存泄漏

对于内存泄漏Xcode提供了Leak工具,但是使用过的人都知道Leak无法查出很多泄漏(如循环引用),在这里检测内存泄漏使用的是微信读书团队 Mr.佘 提供的工具 MLeakFinder。 这里大致讲一下实现原理,当一个VC(或者View)被pop或者被dismiss 2 秒后还没有被销毁则认定该VC(或View)发生了泄漏。那如何知道 2 秒后该对象有没有被释放呢,

+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{[self swizzleSEL:@selector(popViewControllerAnimated:) withSEL:@selector(swizzled_popViewControllerAnimated:)];});
}

通过方法交换将系统的pop方法换掉,然后注入自己的代码实现,当调用pop时会调用 willDealloc 方法,该方法实现如下:

- (BOOL)willDealloc {__weak id weakSelf = self;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{__strong id strongSelf = weakSelf;[strongSelf assertNotDealloc];});return YES;
}

通过弱引用持有自身,并在 2 秒后调用 assertNotDealloc, 如果 2 秒内该对象已释放这里的 weakSelf 为nil,也就什么都不会发生,反之则认为发生了内存泄漏,进行下一步操作,如弹出警告等。 这里只是大致讲一下 MLeakFinder 的原理,详细介绍可以去 他的博客 详细了解。

查找循环引用

查找循环引用使用的是 Facebook 开源库 FBRetainCycleDetector ,具体也可以去网上查找相关资料,这里就不详细说。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3+echarts:Vue中使用echarts从后端获取数据并赋值显示

//由于前后端交互,所以使用axios发送请求 const Count ref(null); //设备种类数值 const Name ref(null); //设备种类名称 //设备种类 饼图 const pieChart () > {const getpieChart echarts.init(document.getElementById("deviceKind"));// 创建图标getpieC…

使用 Matlab 拟合函数

1 加载数据 主页—>新建变量 粘贴 X 坐标&#xff0c;重命名变量名 同样的步骤&#xff0c;新建变量&#xff0c;加入 y 值 2 多项式拟合 打开APP&#xff0c;在数学工具里面选择--------》Curve Fitting 3 加载数据&#xff0c;选择功能

k8s中cert-manager管理https证书

前言 目前https是刚需,但证书又很贵,虽然阿里云有免费的,但没有泛域名证书,每有一个子域名就要申请一个证书,有效期1年,1年一到全都的更换,太麻烦了。经过搜索,发现了自动更新证书神器cert-manager;当然cert-manager是基于k8s的。 安装采用Helm方式 Chart地址: ht…

蓝桥杯刷题day06——平均

1、题目描述 有一个长度为n 的数组&#xff08;n 是 10 的倍数&#xff09;&#xff0c;每个数ai都是区间 [0,9] 中的整数。 小明发现数组里每种数出现的次数不太平均&#xff0c;而更改第i 个数的代价为bi&#xff0c; 他想更改若干个数的值使得这10 种数出现的次数相等&…

ArcGIS学习(五)坐标系-2

3.不同基准面坐标系之间的转换 在上一关中,我们学习了ArcGIS中的投影(投影栅格)工具,并以"WGS1984地理坐标系与WGS1984的UTM投影坐标系的转换”为例进行讲解。 "WGS1984地理坐标系与WGS1984的UTM投影坐标系的转换”代表的是同一个基准面下的两个坐标的转换。 …

微服务-微服务Alibaba-Nacos 源码分析 (源码流程图)-2.0.1

客户端注册临时实例&#xff0c;GRPC处理 客户端服务发现 及订阅处理 客户端数据变换&#xff0c;数据推送&#xff0c;服务端集群服务数据同步

vulhub中Adminer ElasticSearch 和 ClickHouse 错误页面SSRF漏洞复现(CVE-2021-21311)

Adminer是一个PHP编写的开源数据库管理工具&#xff0c;支持MySQL、MariaDB、PostgreSQL、SQLite、MS SQL、Oracle、Elasticsearch、MongoDB等数据库。 在其4.0.0到4.7.9版本之间&#xff0c;连接 ElasticSearch 和 ClickHouse 数据库时存在一处服务端请求伪造漏洞&#xff08…

20240206三次握手四次挥手

TCP和UDP异同点 相同点&#xff1a;同属于传输层的协议 不同点&#xff1a; TCP ----> 稳定 1> 提供面向连接的&#xff0c;可靠的数据传输服务 2> 传输过程中&#xff0c;数据无误、数据无丢失、数据无失序、数据无重复 1、TCP会给每个数据包编上编号&#xff…

计算机网络-华为无线网络配置

前面已经大致了解了无线通信的原理和无线组网的概念&#xff0c;今天来学习无线的配置过程与步骤。 一、无线组网配置流程 在开始配置前复习下前面讲过无线组网有涉及几个设备&#xff0c;AC无线控制器、AP无线接入点、POE交换机。无线组网与有线组网是相对独立的&#xff0c;不…

Python tkinter (15) —— PhotoImage

本文主要介绍Python tkinter PhotoImage图像应用及示例。 系列文章 python tkinter窗口简单实现 Python tkinter (1) —— Label标签 Python tkinter (2) —— Button标签 Python tkinter (3) —— Entry标签 Python tkinter (4) —— Text控件 Python tkinter (5) 选项按…

计算机网络-流量控制(数据链路层的流量控制及与传输层流量控制的区别 流量控制的方法 可靠传输,滑动窗口,流量控制三者关系)

文章目录 数据链路层的流量控制及与传输层流量控制的区别流量控制的方法各方法对应的发生窗口和接收窗口大小 可靠传输&#xff0c;滑动窗口&#xff0c;流量控制三者关系小结 数据链路层的流量控制及与传输层流量控制的区别 端到端&#xff1a;两个主机之间的 点对点&#xf…

idea设置terminal为git

要在IntelliJ IDEA中设置终端为Git Bash&#xff0c;请按照以下步骤操作&#xff1a; 打开 Settings&#xff08;设置&#xff09;。点击 Tools&#xff08;工具&#xff09;选项卡。进入 Terminal&#xff08;终端&#xff09;界面。在 Shell Path 下选择 Browse&#xff08;…

51单片机基础:定时器

1.定时器介绍 51单片机通常有两个定时器&#xff1a;定时器 0/1&#xff0c;好一点的可能有定时器3。 在介绍定时器之前我们先科普下几个知识&#xff1a; 1&#xff0c;CPU 时序的有关知识 ①振荡周期&#xff1a;为单片机提供定时信号的振荡源的周期&#xff08;晶振周期或…

golang 引入swagger(iris、gin)

golang 引入swagger&#xff08;iris、gin&#xff09; 在开发过程中&#xff0c;我们不免需要调试我们的接口&#xff0c;但是有些接口测试工具无法根据我们的接口变化而动态变化。文档和代码是分离的。总是出现文档和代码不同步的情况。这个时候就可以在我们项目中引入swagge…

Linux的打包压缩与解压缩---tar、xz、zip、unzip

最近突然用到了许久不用的压缩解压缩命令&#xff0c;真的陌生&#xff0c; 哈哈&#xff0c;记录一下&#xff0c;后续就不用搜索了。 tar的打包 tar -cvf 压缩有的文件名称 需要压缩的文件或文件夹tar -cvf virtualbox.tar virtualbox/ tar -zcvf virtualbox.tar virtualbo…

NX/UG二次开发—其他—矩形套料(排料)简介

算法逻辑 排料方法一定时间内获取近似解的算法 看了一些论文和博客&#xff0c;一般排料方法采用最低水平线算法排料&#xff0c;再此基础上增加空余区域填充。 然后配合遗传学算法||模拟退火算法||蚁群算法||免疫算法等&#xff0c;在一定时间内求得一组最优解。 在最简单的…

React+Antd+tree实现树多选功能(选中项受控+支持模糊检索)

1、先上效果 树型控件&#xff0c;选中项形成一棵新的树&#xff0c;若父选中&#xff0c;子自动选中&#xff0c;子取消&#xff0c;父不取消&#xff0c;子选中&#xff0c;所有的父节点自动取消。同时支持模糊检索&#xff0c;会检索出所有包含该内容的关联节点。 2、环境准…

嵌入式学习Day14 C语言 --- 位运算

位运算 注意&#xff1a;符号位也遵循这个规则 一、按位与(&) 运算规则&#xff1a;一假则假 int a 0x33;a & 0x55;0011 00110101 0101 &----------0001 0001 //0x11 二、按位或(|) 运算规则&#xff1a;一真则真 int a 0x33;a |0x55;0011 00110101 0101 |…

使用Python语言生成区块链地址

# 单次运行 import binascii import sha3 from ecdsa import SigningKey, SECP256k1priv SigningKey.generate(curveSECP256k1) # 生成私钥 pub priv.get_verifying_key() # 生成公钥keccak sha3.keccak_256() keccak.update(pub.to_string()) # keccak_256哈希运算 addr…

Elasticsearch:使用 Inference API 进行语义搜索

在我之前的文章 “Elastic Search 8.12&#xff1a;让 Lucene 更快&#xff0c;让开发人员更快”&#xff0c;我有提到 Inference API。这些功能的核心部分始终是灵活的第三方模型管理&#xff0c;使客户能够利用当今市场上下载最多的向量数据库及其选择的转换器模型。在今天的…