简介

OOMDetector完成了FOOM监控,内存阈值OOM监控和内存分配监控

APM - iOS OOM监控 OOMDetector代码解析

流程图

APM - iOS OOM监控 OOMDetector代码解析

经过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

排除法判断流程

APM - iOS OOM监控 OOMDetector代码解析

调用

- (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内存的深化排查和优化