简介
OOMDetector完成了FOOM监控,内存阈值OOM监控和内存分配监控
流程图
经过Hook IOS体系底层内存分配的相关办法(包括malloc_zone相关的堆内存分配以及vm_allocate对应的VM内存分配办法),盯梢并记载进程中每个对象内存的分配信息,包括分配仓库、累计分配次数、累计分配内存等,这些信息也会被缓存到进程内存中。
在内存触顶的时候,组件会定时Dump这些仓库信息到本地磁盘,这样假如程序爆内存了,就能够将爆内存前Dump的仓库数据上报到后台服务器进行分析
外部依靠
依靠外部提供部分application生命周期,Crash和卡死检测等能力,需求外部注入
- UIApplication delegate需求外部调用
- appDidCrashed需求外部调用
- appDetectDeadLock需求外部调用,卡死5秒以上
- appResumeFromDeadLock,卡死5秒以上恢复时调用
#import <Foundation/Foundation.h>
@interface FOOMMonitor : NSObject
/*! @brief 监控前台爆内存,原理参考:https://code.facebook.com/posts/1146930688654547/reducing-fooms-in-the-facebook-ios-app/ */
+(FOOMMonitor *)getInstance;
/*! @brief 开始爆内存监控,不会影响功用,能够全网敞开 */
-(void)start;
/*! @brief 获取日志uuid,uuid每次发动会唯一生成 */
-(NSString *)getLogUUID;
/*! @brief 获取当时记载的日志信息目录 */
-(NSString *)getLogPath;
/*! @brief 为了确保数据精确,请在UIApplication delegate的applicationDidEnterBackground:回调第一时刻调用该办法 */
-(void)appDidEnterBackground;
/*! @brief 为了确保数据精确,请在UIApplication delegate的applicationWillEnterForeground:回调第一时刻调用该办法 */
-(void)appWillEnterForground;
/*! @brief 为了确保数据精确,请在UIApplication delegate的applicationWillTerminate:回调第一时刻调用该办法 */
-(void)appWillTerminate;
/*! @brief 请在Crash组件捕获到crash后调用该办法 */
-(void)appDidCrashed;
/*! @brief 请在卡死检测组件检测到5秒以上卡登时调用该办法 */
-(void)appDetectDeadLock:(NSDictionary *)stack;
/*! @brief 请在卡死检测组件从5秒以上卡顿恢复时回调该办法 */
-(void)appResumeFromDeadLock;
/*! @brief 假如敞开了OOMDetector爆内存仓库检测请设置该办法 */
-(void)setOOMDetectorOpen:(BOOL)isOpen;
/*! @brief 设置appVersion */
-(void)setAppVersion:(NSString *)appVersion;
/*! @brief SDK内部办法,不要直接调用 */
-(void)updateStage:(NSString *)stage;
-(void)appExit;
@end
工作流程
FOOMMonitor
排除法判断流程
调用
- (void)setupFOOMMonitor
{
[[FOOMMonitor getInstance] setAppVersion:@"OOMDetector_demo"];
//设置爆内存监控,爆内存监控用于监控App前台爆内存和卡死,这个能够全量敞开
[[FOOMMonitor getInstance] start];
}
初始化
- 敞开名叫foomMonitor线程,Timer的处理,mmap处理,监听的处理都在此线程完结
- 开一个Timer
- 注册UIApplicationDidReceiveMemoryWarningNotification的监听
-(id)init{
if(self = [super init]){
_uuid = [self uuid];
_logLock = [NSRecursiveLock new];
_thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil];
[_thread setName:@"foomMonitor"];
_timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(updateMemory) userInfo:nil repeats:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[_thread start];
}
return self;
}
敞开
- 调用createmmapLogger
-(void)start
{
_isDetectorStarted = YES;
if([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
{
_appState = APPENTERBACKGROUND;
}
else {
_appState = APPENTERFORGROUND;
}
_isCrashed = NO;
_isExit = NO;
_isDeadLock = NO;
_ocurTime = [[NSDate date] timeIntervalSince1970];
_startTime = _ocurTime;
[self performSelector:@selector(createmmapLogger) onThread:_thread withObject:nil waitUntilDone:NO];
}
创立存储
履行createmmapLogger
- 依据fishhook,经过对“_exit”和”exit”做rebind_symbols,获取exit事情并记载信息
- 依据methodSwizzle,hook了viewDidAppear,获取并记载自定义ViewController的称号
-(void)createmmapLogger
{
[_logLock lock];
[self hookExitAndAbort];
[self swizzleMethods];
NSString *dir = [self foomMemoryDir];
_systemVersion = [[NSProcessInfo processInfo] operatingSystemVersionString];
_currentLogPath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.oom",_uuid]];
_foomLogger = new HighSpeedLogger(malloc_default_zone(), _currentLogPath, foom_mmap_size);
_crash_stage = @" ";
int32_t length = 0;
if(_foomLogger && _foomLogger->isValid()){
_foomLogger->memcpyLogger((const char *)&length, 4);
}
[self updateFoomData];
[self uploadLastData];
[_logLock unlock];
}
存储的封装
HighSpeedLogger类结构如下,内部依据mmap封装完成
class HighSpeedLogger
{
public:
~HighSpeedLogger();
HighSpeedLogger(malloc_zone_t *zone, NSString *path, size_t mmap_size);
BOOL memcpyLogger(const char *content, size_t length);
void cleanLogger();
void syncLogger();
bool isValid();
LogPrinter logPrinterCallBack;
public:
char *mmap_ptr;
size_t mmap_size;
size_t current_len;
malloc_zone_t *memory_zone;
FILE *mmap_fp;
bool isFailed;
};
记载
updateFoomData
- 校验HighSpeedLogger的状况
- 校验mmap的状况
- 在size够与不行2种情况下,经过memcpy增加进数据
-(void)updateFoomData{
if(_foomLogger && _foomLogger->isValid()){
NSString* residentMemory = [NSString stringWithFormat:@"%lu", (unsigned long)_residentMemSize];
NSDictionary *foomDict = [NSDictionary dictionaryWithObjectsAndKeys:residentMemory,@"lastMemory",[NSNumber numberWithUnsignedLongLong:_memWarningTimes],@"memWarning",_uuid,@"uuid",_systemVersion,@"systemVersion",_appVersion,@"appVersion",[NSNumber numberWithInt:(int)_appState],@"appState",[NSNumber numberWithBool:_isCrashed],@"isCrashed",[NSNumber numberWithBool:_isDeadLock],@"isDeadLock",_deadLockStack ? _deadLockStack : @"",@"deadlockStack",[NSNumber numberWithBool:_isExit],@"isExit",[NSNumber numberWithDouble:_ocurTime],@"ocurTime",[NSNumber numberWithDouble:_startTime],@"startTime",[NSNumber numberWithBool:_isOOMDetectorOpen],@"isOOMDetectorOpen",_crash_stage,@"crash_stage",nil];
NSData *foomData = [NSKeyedArchiver archivedDataWithRootObject:foomDict];
if(foomData && [foomData length] > 0){
_foomLogger->cleanLogger();
int32_t length = (int32_t)[foomData length];
if(!_foomLogger->memcpyLogger((const char *)&length, 4)){
[[NSFileManager defaultManager] removeItemAtPath:_currentLogPath error:nil];
delete _foomLogger;
_foomLogger = NULL;
}
else {
if(!_foomLogger->memcpyLogger((const char *)[foomData bytes],[foomData length])){
[[NSFileManager defaultManager] removeItemAtPath:_currentLogPath error:nil];
delete _foomLogger;
_foomLogger = NULL;
}
}
}
}
}
上报
uploadLastData
- 创立并行行列,异步任务
- foomData转换成字典
- 数据解析组合,增加信息,更换字段,转换成上报数据
- 借助外部能力上报数据(经过署理办法外部注入),铲除原路径下的数据,整理日志
-(void)uploadLastData
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *foomDir = [self foomMemoryDir];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *paths = [fm contentsOfDirectoryAtPath:foomDir error:nil];
for(NSString *path in paths)
{
if([path hasSuffix:@".oom"]){
NSString *fullPath = [foomDir stringByAppendingPathComponent:path];
if([fullPath isEqualToString:_currentLogPath]){
continue;
}
NSData *metaData = [NSData dataWithContentsOfFile:fullPath];
if(metaData.length <= 4){
[fm removeItemAtPath:fullPath error:nil];
continue;
}
int32_t length = *(int32_t *)metaData.bytes;
if(length <= 0 || length > [metaData length] - 4){
[fm removeItemAtPath:fullPath error:nil];
}
else {
NSData *foomData = [NSData dataWithBytes:(const char *)metaData.bytes + 4 length:(NSUInteger)length];
NSDictionary *foomDict = nil;
@try {
foomDict = [NSKeyedUnarchiver unarchiveObjectWithData:foomData];
}
@catch (NSException *e) {
foomDict = nil;
OOM_Log("unarchive FOOMData failed,length:%d,exception:%s!",length,[[e description] UTF8String]);
}
@finally{
if(foomDict && [foomDict isKindOfClass:[NSDictionary class]]){
NSString *uin = [foomDict objectForKey:@"uin"];
if(uin == nil || uin.length <= 0){
uin = @"10000";
}
NSDictionary *uploadData = [self parseFoomData:foomDict];
NSDictionary *aggregatedData = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:uploadData],@"parts",nil];
NSString *uuid = [foomDict objectForKey:@"uuid"];
NSDictionary *basicParameter = [NSDictionary dictionaryWithObjectsAndKeys:uin,@"uin",uuid,@"client_identify",[foomDict objectForKey:@"ocurTime"],@"occur_time",nil];
[[QQLeakFileUploadCenter defaultCenter] fileData:aggregatedData extra:basicParameter type:QQStackReportTypeOOMLog completionHandler:nil];
}
[fm removeItemAtPath:fullPath error:nil];
}
}
}
}
[[OOMDetector getInstance] clearOOMLog];
});
}
OOMDetector
注册回调
[[OOMDetector getInstance] registerLogCallback:oom_log_callback];
调用
- (void)setupOOMDetector
{
OOMDetector *detector = [OOMDetector getInstance];
[detector setupWithDefaultConfig];
//TODO Config
}
默许装备
- setMaxStackDepth
- setNeedSystemStack
- setNeedStacksWithoutAppStack
- 敞开内存阈值监控
- (void)setupWithDefaultConfig
{
CGFloat OOMThreshhold = 300.f;
NSString *platform = [QQLeakDeviceInfo platform];
NSString *prefix = @"iPhone";
if ([platform hasPrefix:prefix]) {
if ([[[[platform substringFromIndex:prefix.length] componentsSeparatedByString:@","] firstObject] intValue] > 7) {
OOMThreshhold = 800.f;
}
}
[self setMaxStackDepth:50];
[self setNeedSystemStack:YES];
[self setNeedStacksWithoutAppStack:YES];
// 敞开内存触顶监控
[self startMaxMemoryStatistic:OOMThreshhold];
// 显现内存悬浮球
[self showMemoryIndicatorView:YES];
}
敞开内存阈值监控
-(void)startMaxMemoryStatistic:(double)overFlowLimit
{
[[OOMStatisticsInfoCenter getInstance] startMemoryOverFlowMonitor:overFlowLimit];
}
- 上报前次数据,依然是依靠外部能力上报数据
- 敞开MemoryOverflowMonitor线程,敞开间隔0.5秒的Timer定时获取和更新内存信息
-(void)startMemoryOverFlowMonitor:(double)overFlowLimit
{
[self uploadLastData];
overflow_limit = overFlowLimit;
_thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil];
[_thread setName:@"MemoryOverflowMonitor"];
_timer = [[NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(updateMemory) userInfo:nil repeats:YES] retain];
[_thread start];
}
-
获取residentMemory和footPrintMemory
- phys_footprint = dirty memory + compressed memory
- resident memory = dirty memory + clean memory loaded in phisical
-
maxMemory会不断更新当时占用的最大内存数,flag约束了30次,即15秒开始
-(void)updateMemory
{
static int flag = 0;
double maxMemory = [self appMaxMemory];
if (self.statisticsInfoBlock) {
self.statisticsInfoBlock(_residentMemSize);
}
double physFootprintMemory = [self physFootprintMemory];
_indicatorView.memory = _residentMemSize;//physFootprintMemory;
NSLog(@"resident:%lfMb footprint:%lfMb",_residentMemSize,physFootprintMemory);
++flag;
if(maxMemory && flag >= 30){
if(maxMemory > _singleLoginMaxMemory){
_singleLoginMaxMemory = maxMemory;
[self saveLastSingleLoginMaxMemory];
flag = 0;
}
}
}
超出内存阈值处理
收集到的信息过少,只要当时内存,内存阈值,首次OOM的时刻,发动时刻(未完成)
- 判断之前数据是否已上报
- 判断是否超出阈值
- 写入本地
-(void)saveLastSingleLoginMaxMemory{
if(_hasUpoad){
NSString* currentMemory = [NSString stringWithFormat:@"%f", _singleLoginMaxMemory];
NSString* overflowMemoryLimit =[NSString stringWithFormat:@"%f", overflow_limit];
if(_singleLoginMaxMemory > overflow_limit){
static BOOL isFirst = YES;
if(isFirst){
_firstOOMTime = [[NSDate date] timeIntervalSince1970];
isFirst = NO;
}
}
NSDictionary *minidumpdata = [NSDictionary dictionaryWithObjectsAndKeys:currentMemory,@"singleMemory",overflowMemoryLimit,@"threshold",[NSString stringWithFormat: @"%.2lf", _firstOOMTime],@"LaunchTime",nil];
NSString *fileDir = [self singleLoginMaxMemoryDir];
if (![[NSFileManager defaultManager] fileExistsAtPath:fileDir])
{
[[NSFileManager defaultManager] createDirectoryAtPath:fileDir withIntermediateDirectories:YES attributes:nil error:nil];
}
NSString *filePath = [fileDir stringByAppendingString:@"/apmLastMaxMemory.plist"];
if(minidumpdata != nil){
if([[NSFileManager defaultManager] fileExistsAtPath:filePath]){
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}
[minidumpdata writeToFile:filePath atomically:YES];
}
}
}
装备
功用能够依据自己的实际需求选择和装备
- 设置捕获仓库数据、内存log署理,在出现单次大块内存分配、检查到内存走漏且时、调用uploadAllStack办法时会触发此回调
[detector setFileDataDelegate:[MyOOMDataManager getInstance]];
- 设置app内存触顶监控数据署理,在调用startMaxMemoryStatistic:敞开内存触顶监控后会触发此回调,回来前一次app运行时单次生命周期内的最大物理内存数据
[detector setPerformanceDataDelegate:[MyOOMDataManager getInstance]];
- 单次大块内存分配监控
[detector startSingleChunkMallocDetector:50 * 1024 * 1024 callback:^(size_t bytes, NSString *stack) {
[[NSNotificationCenter defaultCenter] postNotificationName:kChunkMallocNoti object:stack];
}];
- 敞开内存走漏监控,现在只可检测真机运行时的内存走漏,模拟器暂不支持,这个功用占用的内存较大,主张只在测试阶段运用
// [detector setupLeakChecker];
- 敞开MallocStackMonitor用以监控经过malloc办法分配的内存,会增加8%左右的cpu开销和10Mb内存,所以主张抽样敞开
[detector startMallocStackMonitor:30 * 1024 * 1024 logUUID:[[FOOMMonitor getInstance] getLogUUID]];
//30K以下仓库按10%抽样监控
// OOMDetector *oomdetector = [OOMDetector getInstance];
// [oomdetector setMallocSampleFactor:10];
// [oomdetector setMallocNoSampleThreshold:30*1024];
- 敞开VMStackMonitor用以监控非直接经过malloc办法分配的内存
// 因为startVMStackMonitor:办法用到了私有API __syscall_logger会带来app store审阅不经过的危险,此办法默许只在DEBUG形式下生效,假如
// 需求在RELEASE形式下也可用,请打开USE_VM_LOGGER_FORCEDLY宏,可是切记在提交appstore前将此宏封闭,否则可能会审阅不经过
[detector setVMLogger:(void**)&__syscall_logger];
[detector startVMStackMonitor:30 * 1024 * 1024 logUUID:[[FOOMMonitor getInstance] getLogUUID]];
初始化
OOMDetector的初始化中实例化了COOMDetector得到global_oomdetector,并运用malloc_create_zone创立了叫做“OOMDetector”的global_memory_zone
global_oomdetector完成了下列3个监控敞开的功用
内存分配监控
内存分配办法
运用libmalloc中的malloc来申请内存,其本质是从vmpage映射获取内存。
malloc有一系列相关办法,calloc,ralloc,valloc,malloc_zone_malloc,malloc_zone_calloc, malloc_zone_valloc, malloc_zone_realloc, malloc_zone_batch_malloc等。大内存的分配都是经过scalable_zone进行分配。
关于malloc_logger的调用
void *
malloc_zone_malloc(malloc_zone_t *zone, size_t size)
{
MALLOC_TRACE(TRACE_malloc | DBG_FUNC_START, (uintptr_t)zone, size, 0, 0);
void *ptr;
if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {
internal_check();
}
if (size > MALLOC_ABSOLUTE_MAX_SIZE) {
return NULL;
}
ptr = zone->malloc(zone, size); // if lite zone is passed in then we still call the lite methods
if (malloc_logger) {
malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE, (uintptr_t)zone, (uintptr_t)size, 0, (uintptr_t)ptr, 0);
}
MALLOC_TRACE(TRACE_malloc | DBG_FUNC_END, (uintptr_t)zone, size, (uintptr_t)ptr, 0);
return ptr;
}
malloc_logger结构
typedef void(malloc_logger_t)(uint32_t type,
uintptr_t arg1,
uintptr_t arg2,
uintptr_t arg3,
uintptr_t result,
uint32_t num_hot_frames_to_skip);
extern malloc_logger_t *__syscall_logger; // use this to set up syscall logging (e.g., vm_allocate, vm_deallocate, mmap, munmap)
发动大块内存分配监控
- common_stack_logger替换malloc_logger的默许完成
-(BOOL)startSingleChunkMallocDetector:(size_t)threshholdInBytes callback:(ChunkMallocBlock)callback
{
if(!_enableChunkMonitor){
_enableChunkMonitor = YES;
if(_enableChunkMonitor){
global_oomdetector->startSingleChunkMallocDetector(threshholdInBytes,callback);
self.chunkMallocBlock = callback;
malloc_logger = (malloc_logger_t *)common_stack_logger;
}
}
return _enableChunkMonitor;
}
- 创立CStackHelper
- 重置一次chunkMallocCallback
void COOMDetector::startSingleChunkMallocDetector(size_t threshholdInBytes,ChunkMallocBlock mallocBlock)
{
chunk_threshold = threshholdInBytes;
enableChunkMonitor = YES;
if(chunkMallocCallback != NULL){
Block_release(chunkMallocCallback);
}
if(chunk_stackHelper == NULL){
chunk_stackHelper = new CStackHelper(nil);
}
chunkMallocCallback = Block_copy(mallocBlock);
fileUploadCenter = [QQLeakFileUploadCenter defaultCenter];
}
common_stack_logger
void common_stack_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t backtrace_to_skip)
{
//QQLeak
malloc_stack_logger(type,arg1,arg2,arg3,result,backtrace_to_skip);
//OOMDetector
oom_malloc_logger(type,arg1,arg2,arg3,result,backtrace_to_skip);
}
oom_malloc_logger
其中跟大块内存分配相关逻辑
void oom_malloc_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t backtrace_to_skip)
{
if (global_oomdetector == NULL) {
return;
}
if (type & stack_logging_flag_zone) {
type &= ~stack_logging_flag_zone;
}
if (type == (stack_logging_type_dealloc|stack_logging_type_alloc)) {
if(global_oomdetector->enableChunkMonitor && arg3 > global_oomdetector->chunk_threshold){
global_oomdetector->get_chunk_stack((size_t)arg3);
}
} else if((type & stack_logging_type_alloc) != 0){
if(global_oomdetector->enableChunkMonitor && arg2 > global_oomdetector->chunk_threshold){
global_oomdetector->get_chunk_stack((size_t)arg2);
}
}
//...Codes
}
获取大块内存仓库
- 会用到先前创立的CStackHelper获取image信息
- 假如有署理和回调就履行
void COOMDetector::get_chunk_stack(size_t size)
{
if(enableChunkMonitor){
vm_address_t *stacks[max_stack_depth_sys];
size_t depth = backtrace((void**)stacks, max_stack_depth_sys);
NSMutableString *stackInfo = [[[NSMutableString alloc] init] autorelease];
NSDateFormatter* df1 = [[NSDateFormatter new] autorelease];
df1.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
NSString *dateStr1 = [df1 stringFromDate:[NSDate date]];
[stackInfo appendFormat:@"%@ chunk_malloc:%.2fmb stack:\n",dateStr1,(double)size/(1024*1024)];
for(size_t j = 2; j < depth; j++){
vm_address_t addr = (vm_address_t)stacks[j];
segImageInfo segImage;
if(chunk_stackHelper->getImageByAddr(addr, &segImage)){
[stackInfo appendFormat:@""%lu %s 0x%lx 0x%lx" ",j - 2,(segImage.name != NULL) ? segImage.name : "unknown",segImage.loadAddr,(long)addr];
}
}
[stackInfo appendFormat:@"\n"];
if (fileUploadCenter.fileDataDelegate) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [stackInfo dataUsingEncoding:NSUTF8StringEncoding];
if (data && data.length > 0) {
NSDictionary *extra = [NSDictionary dictionaryWithObjectsAndKeys:[[UIDevice currentDevice] systemVersion],@"systemversion",[QQLeakDeviceInfo platform],@"Device",@"chunk_malloc",@"type",nil];
[fileUploadCenter fileData:data extra:extra type:QQStackReportTypeChunkMemory completionHandler:^(BOOL completed) {
}];
}
});
}
if(chunkMallocCallback)
{
chunkMallocCallback(size,stackInfo);
}
}
}
-
获取image的相关信息
- name
- loadAddress
- beginAddress
- endAddress
bool CStackHelper::getImageByAddr(vm_address_t addr,segImageInfo *image){
for (size_t i = 0; i < allImages.size; i++)
{
if (addr > allImages.imageInfos[i]->beginAddr && addr < allImages.imageInfos[i]->endAddr) {
image->name = allImages.imageInfos[i]->name;
image->loadAddr = allImages.imageInfos[i]->loadAddr;
image->beginAddr = allImages.imageInfos[i]->beginAddr;
image->endAddr = allImages.imageInfos[i]->endAddr;
return true;
}
}
return false;
}
发动Malloc堆内存监控
- common_stack_logger替换malloc_logger的默许完成
-(BOOL)startMallocStackMonitor:(size_t)threshholdInBytes logUUID:(NSString *)uuid
{
if(!_enableOOMMonitor){
_currentDir = [[[self OOMDataPath] stringByAppendingPathComponent:uuid] retain];
_normal_path = [[_currentDir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mmap",uuid]] retain];
init_crc_table_for_oom();
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:_currentDir]) {
[fileManager createDirectoryAtPath:_currentDir withIntermediateDirectories:YES attributes:nil error:nil];
}
if (![fileManager fileExistsAtPath:_normal_path]) {
[fileManager createFileAtPath:_normal_path contents:nil attributes:nil];
}
global_oomdetector->initLogger(global_memory_zone, _normal_path, normal_size);
_enableOOMMonitor = global_oomdetector->startMallocStackMonitor(threshholdInBytes);
if(_enableOOMMonitor){
malloc_logger = (malloc_logger_t *)common_stack_logger;
}
}
return _enableOOMMonitor;
}
- 创立栈哈希表
- 创立指针哈希表
BOOL COOMDetector::startMallocStackMonitor(size_t threshholdInBytes)
{
oom_stacks_hashmap = new CStacksHashmap(50000,global_memory_zone,log_path,log_mmap_size);
oom_stacks_hashmap->oom_threshold = threshholdInBytes;
oom_ptrs_hashmap = new CPtrsHashmap(500000,global_memory_zone);
enableOOMMonitor = YES;
oom_threshold = threshholdInBytes;
return YES;
}
common_stack_logger
void common_stack_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t backtrace_to_skip)
{
//QQLeak
malloc_stack_logger(type,arg1,arg2,arg3,result,backtrace_to_skip);
//OOMDetector
oom_malloc_logger(type,arg1,arg2,arg3,result,backtrace_to_skip);
}
oom_malloc_logger
其中跟堆内存分配相关逻辑
void oom_malloc_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t backtrace_to_skip)
{
if (global_oomdetector == NULL) {
return;
}
if (type & stack_logging_flag_zone) {
type &= ~stack_logging_flag_zone;
}
if (type == (stack_logging_type_dealloc|stack_logging_type_alloc)) {
if (arg2 == result) {
if(global_oomdetector->enableOOMMonitor){
global_oomdetector->removeMallocStack((vm_address_t)arg2);
if(global_oomdetector->sampleFactor > 1){
if(rand()%(global_oomdetector->sampleFactor) != 0 && arg3 < global_oomdetector->sampleThreshold){
return;
}
}
global_oomdetector->recordMallocStack(result, (uint32_t)arg3,2);
}
return;
}
if (!arg2) {
if(global_oomdetector->enableOOMMonitor){
if(global_oomdetector->sampleFactor > 1){
if(rand()%(global_oomdetector->sampleFactor) != 0 && arg3 < global_oomdetector->sampleThreshold){
return;
}
}
global_oomdetector->recordMallocStack(result, (uint32_t)arg3,2);
}
return;
} else {
if(global_oomdetector->enableOOMMonitor){
global_oomdetector->removeMallocStack((vm_address_t)arg2);
if(global_oomdetector->sampleFactor > 1){
if(rand()%(global_oomdetector->sampleFactor) != 0 && arg3 < global_oomdetector->sampleThreshold){
return;
}
}
global_oomdetector->recordMallocStack(result, (uint32_t)arg3,2);
};
return;
}
}
else if (type == stack_logging_type_dealloc) {
if (!arg2) {
return;
}
if(global_oomdetector->enableOOMMonitor){
global_oomdetector->removeMallocStack((vm_address_t)arg2);
}
}
else if((type & stack_logging_type_alloc) != 0){
if(global_oomdetector->enableOOMMonitor){
if(global_oomdetector->sampleFactor > 1){
if(rand()%(global_oomdetector->sampleFactor) != 0 && arg2 < global_oomdetector->sampleThreshold){
return;
}
}
global_oomdetector->recordMallocStack(result, (uint32_t)arg2,2);
}
}
}
recordMallocStack
- 获取堆内存相关信息
void COOMDetector::recordMallocStack(vm_address_t address,uint32_t size,size_t stack_num_to_skip)
{
base_stack_t base_stack;
base_ptr_log base_ptr;
uint64_t digest;
vm_address_t *stack[max_stack_depth];
if(needStackWithoutAppStack){
base_stack.depth = (uint32_t)stackHelper->recordBacktrace(needSysStack,0,0,stack_num_to_skip, stack,&digest,max_stack_depth);
}
else {
base_stack.depth = (uint32_t)stackHelper->recordBacktrace(needSysStack,0,1,stack_num_to_skip, stack,&digest,max_stack_depth);
}
if(base_stack.depth > 0){
base_stack.type = 0;
if(sampleFactor > 1 && size < sampleThreshold){
base_stack.size = size * sampleFactor;
base_stack.count = sampleFactor;
}
else {
base_stack.size = size;
base_stack.count = 1;
}
base_stack.stack = stack;
base_ptr.digest = digest;
base_ptr.size = size;
do_lockHashmap
if(oom_ptrs_hashmap && oom_stacks_hashmap){
if(oom_ptrs_hashmap->insertPtr(address, &base_ptr)){
oom_stacks_hashmap->insertStackAndIncreaseCountIfExist(digest, &base_stack);
}
if(needCleanStackCache && oom_ptrs_hashmap->getRecordNum() > cache_clean_num){
removeTinyMallocStacks(cache_clean_threshold);
cache_clean_num += oom_ptrs_hashmap->getRecordNum();
}
}
do_unlockHashmap
}
}
发动VMstack内存监控
用于非Malloc分配的内存
- 运用oom_vm_logger替换*(global_oomdetector->vm_sys_logger),*(global_oomdetector->vm_sys_logger)就是刚才装备的(void**)&__syscall_logger
[detector setVMLogger:(void**)&__syscall_logger];
-(void)setVMLogger:(void**)logger
{
global_oomdetector->vm_sys_logger = (malloc_logger_t**)logger;
}
-(BOOL)startVMStackMonitor:(size_t)threshHoldInbytes logUUID:(NSString *)uuid
{
if(!_enableVMMonitor){
_normal_vm_path = [[_currentDir stringByAppendingPathComponent:[NSString stringWithFormat:@"vm_%@.mmap",uuid]] retain];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:_currentDir]) {
[fileManager createDirectoryAtPath:_currentDir withIntermediateDirectories:YES attributes:nil error:nil];
}
if (![fileManager fileExistsAtPath:_normal_vm_path]) {
[fileManager createFileAtPath:_normal_vm_path contents:nil attributes:nil];
}
global_oomdetector->initLogger(global_memory_zone, _normal_vm_path, normal_size);
_enableVMMonitor = global_oomdetector->startVMStackMonitor(threshHoldInbytes);
if(_enableVMMonitor){
if(global_oomdetector->vm_sys_logger != NULL)
{
*(global_oomdetector->vm_sys_logger) = oom_vm_logger;
}
}
}
return YES;
}
- 创立栈哈希表
- 创立指针哈希表
BOOL COOMDetector::startVMStackMonitor(size_t threshholdInBytes)
{
oom_vm_stacks_hashmap = new CStacksHashmap(1000,global_memory_zone,log_path,log_mmap_size);
oom_vm_stacks_hashmap->oom_threshold = threshholdInBytes;
oom_vm_ptrs_hashmap = new CPtrsHashmap(10000,global_memory_zone);
enableVMMonitor = YES;
vm_threshold = threshholdInBytes;
// rebind_symbols((struct rebinding[2]){
// {"mmap",(void*)new_mmap,(void**)&orig_mmap},
// {"munmap", (void*)new_munmap, (void **)&orig_munmap}},
// 2);
return YES;
}
oom_vm_logger
void oom_vm_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t backtrace_to_skip)
{
if(type & stack_logging_type_vm_allocate){ //vm_mmap or vm_allocate
type = (type & ~stack_logging_type_vm_allocate);
type = type >> 24;
if((type >= 1 && type <= 11) || type == 32 || arg2 == 0 || type == 35){
return;
}
// const char *flag = "unknown";
// if(type <= 89){
// flag = vm_flags[type];
// }
global_oomdetector->recordVMStack(vm_address_t(result), uint32_t(arg2), 2);
}
else if(type & stack_logging_type_vm_deallocate){ //vm_deallocate or munmap
type = (type & ~stack_logging_type_vm_allocate);
type = type >> 24;
if((type >= 1 && type <= 11) || type == 32 || arg2 == 0 || type == 35){
return;
}
global_oomdetector->removeVMStack(vm_address_t(arg2));
}
}
VMStack内存的处理
void COOMDetector::recordVMStack(vm_address_t address,uint32_t size,size_t stack_num_to_skip)
{
base_stack_t base_stack;
base_ptr_log base_ptr;
uint64_t digest;
vm_address_t *stack[max_stack_depth];
base_stack.depth = (uint32_t)stackHelper->recordBacktrace(YES,1,0,stack_num_to_skip, stack,&digest,max_stack_depth);
if(base_stack.depth > 0){
base_stack.type = 1;
base_stack.size = size;
base_stack.count = 1;
base_stack.stack = stack;
base_ptr.digest = digest;
base_ptr.size = size;
do_lockVMHashmap
if(oom_vm_ptrs_hashmap && oom_vm_stacks_hashmap){
oom_vm_ptrs_hashmap->insertPtr(address, &base_ptr);
oom_vm_stacks_hashmap->insertStackAndIncreaseCountIfExist(digest, &base_stack);
}
do_unlockVMHashmap
}
}
void COOMDetector::removeVMStack(vm_address_t address)
{
do_lockVMHashmap
if(oom_vm_ptrs_hashmap && oom_vm_stacks_hashmap){
uint32_t size = 0;
uint64_t digest = 0;
if(oom_vm_ptrs_hashmap->removePtr(address,&size,&digest)){
oom_vm_stacks_hashmap->removeIfCountIsZero(digest, size, 1);
}
}
do_unlockVMHashmap
}
引用
Reducing FOOMs in the Facebook iOS app
【腾讯开源】iOS爆内存问题解决方案-OOMDetector组件
关于iOS内存的深化排查和优化