OOM:outOfMemory,App内存占用达到上限要被系统强杀了。由iOS的Jetsam机制导致的一种“另类”崩溃,并且日志无法通过信号捕捉到。
Jestam机制: 操作系统为了防止内存资源的过度使用,而采用的一种管控机制。
通过JetsamEvent 日志计算内存限制值
通过手机的设置 —> 隐私 —> 分析中看到相关日志,日志中的PageSize 代表内存页大小 ,rpages内存页数量。所以:pagesize *rpages/1024/1024 就是内存大小。目前为1.4GB。
iOS怎么发现Jetsam?
iOS会开启优先级最高的线程vm_pressure_monitor 来监控系统的内存压力情况,通过一个堆栈来维护所有App的进程。
而且iOS还会维护一个内存快照表,用于保存每个进程内存页的消耗情况。
当监控系统内存的线程某App内存有压力,就会发通知让App执行DidReceiveMemoryWarning代理。
通过此代理,可以获得最后一个机会去释放内存。
系统在强杀App前,会先做优先级判断。
优先级判断依据是:内核用线程的优先级最高,其次是操作系统,最后是App。前台App优先级高于后台App。
若相同优先级的线程中,哪个线程 CPU 占用高,优先级会比降低。
iOS因为系统占用原因强杀App前,至少有6秒钟的时间用来做优先级判断。也就是这6秒内生成JetSamEvent日志。
除了JetSamEvent日志,还可以通过XNU来获取内存的限制值。
通过XNU获取内存限制值
XNU:iOS系统的内核。
在XNU中,有专门获取内存上限值的函数和宏。也可以通过memorystatus_priority_entry 这个结构体得到进程的优先级与内存限制值。
通过XNU的宏获取内存限制,需要由root权限,所以非越狱情况下想获取这个权限,只能利用didReceivedMemoryWarning 这个代理事件来动态获取。
如何获取当前内存使用情况?
iOS提供了一个函数task_info,可以帮我们获取到当前任务的信息。
//需要导入 才能使用
struct mach_task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
float used_memory = info.resident_size;
NSLog(@"使用了 %f MB内存",used_memory / 1024.0f / 1024.0f);
定位内存问题信息收集
现在有三种方法获取内存上限:JetSam日志自己算、XNU不过不越狱就没权限需要通过DidreceivedMemory动态获取、task_info只需要导入mach.h文件。
想精确定位问题,就需要dump出完整的内存信息,包括所有对象和其内存占用值,然后在内存接近上限时都记录下来并找机会上传。
不过不仅要知道对象的内存使用,还要知道是谁分配的这块内存,也就是具体在哪个函数里被创建出来的。
内存分配函数malloc(分配后不清理缓存)与calloc(分配后清理缓存)等默认使用nano_zone分配256B以下的内存,大于256B的使用scalable_zone来分配。
小内存的不分析了,毕竟内存超出使用大都是大头原因。
scalable_zone分配内存的函数都会调用malloc_logger函数,系统通过malloc_logger来统计并管理内存的分配情况。
iOS通过堆栈管理所有的app进程,通过一个优先级最高的线程去监控系统内存的压力,还有一个快速对照表记录所有app的内存使用情况。如果内存有压力了,就按照优先级去释放优先级低还使用内存多的。
所以app得内存阈值是没有固定大小的。