关键词:
最近在搞app的性能监控。主要从启动耗时,首屏耗时,操作耗时的几个指标进行监控,后续会增加其他维度的指标
启动耗时
启动耗时主要分为冷启动,热启动。 其中冷启动又分为首次启动,非首次启动。
冷启动:从main函数开始,到第一个用户自定义的页面出现为止(备注:这个过程中要区分一下是否是首次启动)
热启动:从app即将进入前台到,app进入前台的这个过程。涉及到的函数。具体下:
- (void)applicationWillEnterForeground:(UIApplication *)application
// 此处调用热启动开始的方法
- (void)applicationDidBecomeActive:(UIApplication *)application
// 此处调用热启动结束的统计方法
首屏耗时
首屏耗时是指从一个页面初始化到到页面获取数据后第一次渲染完成结束。
初始化的时机:hook UIViewController 的init方法,获取调用的时机
示例代码如下:
- (instancetype)performance_init
NSString *className = NSStringFromClass([self class]);
//过滤掉系统类
if ([className hasPrefix:@"UI"])
return [self performance_init];
// 过滤掉黑名单
if ([JKPerformanceManager isInBlackList:className])
return [self performance_init];
JKPagePerformanceModel *firstScreen_pagePerformanceModel = [JKPagePerformanceModel new];
firstScreen_pagePerformanceModel.element_type = JKPageTypeFirstScreen;
firstScreen_pagePerformanceModel.start_time = [[NSDate date] timeIntervalSince1970] * 1000;
firstScreen_pagePerformanceModel.page = className;
self.firstScreen_pagePerformanceModel = firstScreen_pagePerformanceModel;
return [self performance_init];
渲染完成的时机:目前只考虑有列表的视图。UITableView,UICollectionView reloadData结束后 visibleCell的数量大于某个数值。认为渲染完成。
关于如何reloaddata后获取获取visibleCell的数量可以参考《iOS开发获取tableView,collectionView reloaddata 执行结束布局生效时机》
坑点:如果是嵌套的UITableView,UICollectionView 存在reloaddata的时候,对应的视图的window不存在,这个时候采取延迟满足的策略,为这些视图打上标记。等到这些视图调用didMoveToWindow 的时候,判断视图的window是否存在,如果存在,并且该视图是被标记过的,那么这个时机可以认为是首屏渲染完成的时机。示例代码如下:
- (void)performance_didMoveToWindow
[self performance_didMoveToWindow];
//视图离开页面的时候window为nil,此时过滤掉
if (!self.window)
return;
id delegate = self.delegate;
//过滤掉异常
if (!delegate)
return;
NSString *delegate_ClassName = NSStringFromClass([delegate class]);
// 过滤掉系统子类
if (![NSStringFromClass([self class]) isEqualToString:@"UITableView"])
return;
// 过滤掉系统视图
if ([delegate_ClassName hasPrefix:@"UI"])
return;
// 忽略掉黑名单
if ([JKPerformanceManager isInBlackList:delegate_ClassName])
return;
[self setNeedsLayout];
[self layoutIfNeeded];
if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_tableViewDidMoveToWindow:)])
[[JKPerformanceManager helper] track_tableViewDidMoveToWindow:self];
[self performance_firtScreenTrack];
操作耗时
操作耗时是指:用户的一次操作,比如点击 从开始执行,到执行结束的耗时。我这次处理的操作耗时主要有,按钮的点击,手势触发的时间。列表点击触发的事件。
UIControl的处理如下:
- (void)performance_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
JKOperationPerformanceModel *operatePerformanceModel = [VVOperationPerformanceModel new];
operatePerformanceModel.start_time = [[NSDate date] timeIntervalSince1970] * 1000;
if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_control:sendAction:to:forEvent:)])
[[JKPerformanceManager helper] track_control:self sendAction:action to:target forEvent:event];
// 正常执行事件
[self performance_sendAction:action to:target forEvent:event];
// 忽略掉黑名单,避免干扰
NSString *target_ClassName = NSStringFromClass([target class]);
if ([JKPerformanceManager isInBlackList:target_ClassName])
return;
// 性能采集打点
operatePerformanceModel.end_time = [[NSDate date] timeIntervalSince1970] * 1000;
operatePerformanceModel.element_type = JKOperateTypeClick;
operatePerformanceModel.widget = [NSString stringWithFormat:@"%@+%@",target_ClassName,NSStringFromSelector(action)];
if (!self.track_containerVC)
UIViewController *track_containerVC = [JKPerformanceManager topContainerViewControllerOfResponder:self];
self.track_containerVC = track_containerVC;
operatePerformanceModel.page = NSStringFromClass(self.track_containerVC.class)?:NSStringFromClass([UIViewController class]);
[JKPerformanceManager trackPerformance:operatePerformanceModel vc:self.track_containerVC];
UIGestureRecognizer的处理如下:
- (void)handleOfTarget:(id)target selector:(SEL)selector
// 过滤掉已经hook的
NSMutableArray *hookedArray = (NSMutableArray *)objc_getAssociatedObject(target, track_gesture_target_has_hookedKey);
if ([hookedArray containsObject:selectorStr])
return;
NSError *error1 = nil;
[(NSObject *)target aspect_hookSelector:selector withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> data)
if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_gesture:target:selector:)])
[[JKPerformanceManager helper] track_gesture:self target:target selector:selector];
JKOperationPerformanceModel *operatePerformanceModel = [JKOperationPerformanceModel new];
operatePerformanceModel.start_time = [[NSDate date] timeIntervalSince1970] * 1000;
NSInvocation *invocation = [data originalInvocation];
[invocation invoke];
if ([self isKindOfClass:[UITapGestureRecognizer class]]
|| [self isKindOfClass:[UILongPressGestureRecognizer class]])
operatePerformanceModel.end_time = [[NSDate date] timeIntervalSince1970] * 1000;
if ([self isKindOfClass:[UITapGestureRecognizer class]])
operatePerformanceModel.element_type = JKOperateTypeClick;
else if ([self isKindOfClass:[UILongPressGestureRecognizer class]])
operatePerformanceModel.element_type = JKOperateTypeLongPress;
operatePerformanceModel.widget = [NSString stringWithFormat:@"%@+%@",target_ClassName,selectorStr];
if (!self.view.track_containerVC)
UIViewController *track_containerVC = [JKPerformanceManager topContainerViewControllerOfResponder:self.view];
self.view.track_containerVC = track_containerVC;
operatePerformanceModel.page = NSStringFromClass(self.view.track_containerVC.class)?:NSStringFromClass([UIViewController class]);
[JKPerformanceManager trackPerformance:operatePerformanceModel vc:self.self.view.track_containerVC];
error:&error1];
if (error1)
#if DEBUG
NSLog(@"UIGestureRecognizer+JKTrack error1:%@",error1);
NSAssert(NO, @"UIGestureRecognizer+JKTrack error1");
#endif
else
NSMutableArray *array = (NSMutableArray *)objc_getAssociatedObject(target, track_gesture_target_has_hookedKey);
if (!array)
array = [NSMutableArray new];
[array addObject:selectorStr];
objc_setAssociatedObject(target, track_gesture_target_has_hookedKey, array, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
UITableView 的处理如下:
- (void)performance_reloadData
[self performance_reloadData];
//屏蔽掉系统子类
if (![NSStringFromClass([self class]) isEqualToString:@"UITableView"])
return;
NSString *delegate_ClassName = NSStringFromClass([self.delegate class]);
// 过滤掉系统视图
if ([delegate_ClassName hasPrefix:@"UI"])
return;
// 忽略掉黑名单
if ([JKPerformanceManager isInBlackList:delegate_ClassName])
return;
if (!self.window)
if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_markTableViewNOSuperView:)])
[[JKPerformanceManager helper] track_markTableViewNOSuperView:self];
return;
[self setNeedsLayout];
[self layoutIfNeeded];
if ([[JKPerformanceManager helper] respondsToSelector:@selector(track_reloadDataOfTableView:)])
[[JKPerformanceManager helper] track_reloadDataOfTableView:self];
[self performance_firtScreenTrack];
文中贴出来的代码,主要是方便大家了解一下思路。
完整代码下载地址:《JKPerformanceManager》
《iOS开发获取tableView,collectionView reloaddata 执行结束布局生效时机》
https://blog.csdn.net/hanhailong18/article/details/105243778?spm=1001.2014.3001.5501
《UIView /UIViewController的生命周期》
https://juejin.cn/post/6844904089348734983
《VC耗时时监控的参考资料》
https://blog.csdn.net/killer1989/article/details/108099921
《iOS App冷启动治理:来自美团外卖的实践》
https://segmentfault.com/a/1190000017298001
更多干货文章,欢迎扫描二维码关注功能
ios性能监控方案设计(代码片段)
最近在搞app的性能监控。主要从启动耗时,首屏耗时,操作耗时的几个指标进行监控,后续会增加其他维度的指标启动耗时 启动耗时主要分为冷启动,热启动。其中冷启动又分为首次启动,非首次启动。冷... 查看详情
性能监控之telegraf+influxdb+grafanalinux服务器实时监控(代码片段)
文章目录一、引言二、目标三、解决方案1、Telegraf2、整体设计四、Telegraf&InfluxDB集成1、下载安装2、创建influxDB用户和数据库3、配置Telegraf4、启动Telegraf5、查看数据五、InfluxDB&Grafana集成1、Grafana新建数据源2、下载看板模板... 查看详情
[转帖]java性能监控工具之jprofiler(代码片段)
...性能测试存在的一些问题性能测试工具选型、及jvm调优、方案设计非重要,否则会做很多无用功。 三、Java代码监控及分析工具3.1jporfiler工具描述:Jprofiler java开发软件性能测试工具方便开发期测试开发软件的性能,提高开... 查看详情
支付宝架构解析:ios客户端启动性能优化初探(代码片段)
...分拆解客户端在“容器化框架设计”、“网络优化”、“性能启动优化”、“自动化日志收集”、“RPC组件设计”、“移动应用监控、诊断、定位”等具体实现,带领大家进一步了解支付宝在客户端架构上的迭代与优化历程... 查看详情
快手开源koom浅析,一个高性能线上内存监控方案(代码片段)
转载地址:https://juejin.cn/post/6982121209144016910KOOM相比较LeakCanary和Matrix来说有点不同,后俩者由于dump的整个过程会影响到主进程,所以基本应用与线下监控,而KOOM提出了forkdump的概念,能在dump分析内存泄漏的时... 查看详情
android性能优化之疑难杂症解决方案,u-apm的性能监控分析(代码片段)
...;在各项功能十分成熟的情况下,我们越来越重视App的性能优化,以及用户体验,这关乎一个线上应用的DAU持续增长的基础,以及用户口碑的问题,今天刘某人带大家来一起分析一下崩溃/卡顿/ANR/OOM/启动慢等问... 查看详情
前度监控(埋点)设计方案(代码片段)
...控异常监控常用的埋点方案前端埋点方案选型和前端上报方案设计前端监控结果可视化展示系统的设计为什么需要前端监控(目的是什么)?我们知道,现在互联网产品的获客成本每年都在不断的攀升;比如... 查看详情
zabbix监控部署!(代码片段)
...集中的Web管理界面,它具备的主要功能有:主机性能监控、网络设备性能监控、数据库性能监控、ftp等通用协议的监控 查看详情
关于android性能监控matrix那些事?你知道那些(上)?(代码片段)
前两天录制了两节关于Android性能监控Matrix的视频。1.面试中问道线上性能监控怎么办,Android线上监控种种2.Matrix卡顿监控,函数自动埋点监控方案但是还没有完全录制完全。稍后出~今天先文字分析一下关于Matrix的种种文... 查看详情
nginx可视化管理和监控神器(代码片段)
...数据库知识需求nginx可视化管理,例如 -配置管理 -性能监控 -日志监控 -其他配置方案目前已实现前两条:配置管理,和性能监控 日志分析监控这块还需要另找方案实现!目前方案直接套用github大神开发的nginx-gui gi... 查看详情
zabbix基本概述(代码片段)
...统负载等3.服务监控。如apache,nginx,tomcat,redis,TCP连接数等4.性能监控。如网站性能,服务器性能,数据库性能5.日志监控。如访问日志,错误日志6.安全监控。如用户登录数,本地文件改动,passwd文件变化7.网络监控。如端口,SMTP,... 查看详情
关于android性能监控matrix那些事?你知道那些(中)?(代码片段)
昨天更新了关于Android性能监控Matrix那些事?你知道那些(上)?说的的视频也更新了:微信Matrix卡顿监控实战,函数自动埋点监控方案今天我们接着聊下文:4.Hprof文件分析5.卡顿监控6.卡顿监控源码解析7.插... 查看详情
神器jmh+arthas性能监控(代码片段)
来源| bryantchang.github.io/2019/12/08/java-profile-tools/问题描述JMH简介Arthas我的代码在运行时到底做了什么实际操作监控方法调用trace命令&jad命令watch命令最近的工作日并不算太平,各种大大小小的case和解case,发现已经有好... 查看详情
关于android性能监控matrix那些事?你知道那些?(完)(代码片段)
关于Android性能监控Matrix那些事?你知道那些?(上)关于Android性能监控Matrix那些事?你知道那些(中)?视频也更新了:微信Matrix卡顿监控实战,函数自动埋点监控方案今天抽空把后面的... 查看详情
部署zabbix4.0监控系统(代码片段)
...可。Zabbix具备创建商业监控软件所具备的功能,例如主机性能监控、网络设备性能监控、数据库性能监控、ftp等通用协议的监控,能够利用灵活的可定制警告机制,允许用户对事件发送基于E-mail的警告 查看详情
轻松部署zabbix集中监控系统(代码片段)
...统管理员快速定位/解决存在的各种问题。zabbix功能主机性能监控网络设备性能监控数据库性能监控ftp等通用协议监控可定制灵活警告机制zabbix监控项目CPU负荷内存使用磁盘使用网络状况端口 查看详情
监控io,网络,端口,抓包(代码片段)
监控io性能iostat和sar使用同一个包sysstat[[email protected]~]#iostat-xLinux3.10.0-514.el7.x86_64(aminglinux-02)2017年07月23日_x86_64_(1CPU)avg-cpu:%user%nice%system%iowait%steal%idle1.750.002.494.830.0090.93Dev 查看详情
高并发监控[一]:tp90tp99耗时监控设计与实现(代码片段)
背景性能测试中,我们经常选择TP90、TP95、TP99等指标项作为性能对比的参考水位,在本文中,我们给出一种计算TP90、TP95和TP99等水位线的方法,首先我们解释一下TP90、TP95、TP99的含义.TP90:即90%的数据都满足某一条件.TP95:即95%的数据都... 查看详情