iOS 获取设备占用内存,剩余内存

获取应用占用内存

第一种:

#import <mach/mach.h>
- (int64_t)memoryUsage {
    int64_t memoryUsageInByte = 0;
    struct task_basic_info taskBasicInfo;
    mach_msg_type_number_t size = sizeof(taskBasicInfo);
    kern_return_t kernelReturn = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t) &taskBasicInfo, &size);
    
    if(kernelReturn == KERN_SUCCESS) {
        memoryUsageInByte = (int64_t) taskBasicInfo.resident_size;
        NSLog(@"Memory in use (in bytes): %lld", memoryUsageInByte);
    } else {
        NSLog(@"Error with task_info(): %s", mach_error_string(kernelReturn));
    }
    
    return memoryUsageInByte;
}

使用时发现这个和Xcode中大小不一致,在正确地获取 iOS 应用占用的内存文章中介绍的phys_footprint获取方式更接近,所以更推荐使用第二种。

第二种:

#import <mach/mach.h>
- (int64_t)memoryUsage {
    int64_t memoryUsageInByte = 0;
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    kern_return_t kernelReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
    if(kernelReturn == KERN_SUCCESS) {
        memoryUsageInByte = (int64_t) vmInfo.phys_footprint;
        NSLog(@"Memory in use (in bytes): %lld", memoryUsageInByte);
    } else {
        NSLog(@"Error with task_info(): %s", mach_error_string(kernelReturn));
    }
    return memoryUsageInByte;
}

补充:
在腾讯的OOMDetector内存检测组件中使用的是第一种方式,在Issues中app使用内存计算的与xcode计算的不一致问题中介绍说:ios有memory compress机制,所以实际占用的物理内存会比app分配的内存少。
但是本人在项目中更倾向于使用第二种获取方式

APP可用的总内存

#import <mach/mach.h>
#import <os/proc.h>


// 获取进程可用内存 单位 (Byte)
+ (NSInteger)memoryUsageLimitByByte {
    NSInteger memoryLimit = 0;
    // 获取当前内存使用数据
    if (@available(iOS 13.0, *)) {
        task_vm_info_data_t vmInfo;
        mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
        kern_return_t kr = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&vmInfo, &count);
        if (kr == KERN_SUCCESS) {
            // 间接获取一下当前进程可用的最大内存上限
            // iOS13+可以这样计算:当前进程占用内存+还可以使用的内存=上限值
            NSInteger memoryCanBeUse = (NSInteger)(os_proc_available_memory());
            if (memoryCanBeUse > 0) {
                NSInteger memoryUsed = (NSInteger)(vmInfo.phys_footprint);
                memoryLimit = memoryUsed + memoryCanBeUse;
            }
        }
    }
    if (memoryLimit <= 0) {
        NSInteger deviceMemory = [NSProcessInfo processInfo].physicalMemory;
        memoryLimit = deviceMemory * 0.55;
    }
    if (memoryLimit <= 0) {
        // 这个值一般要小很多, 上面都获取不到才使用
        mach_port_t host_port = mach_host_self();
        mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
        vm_size_t page_size;
        vm_statistics_data_t vm_stat;
        kern_return_t kr;
        kr = host_page_size(host_port, &page_size);
        if (kr == KERN_SUCCESS) {
            kr = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
            if (kr == KERN_SUCCESS)            {
                memoryLimit = vm_stat.free_count * page_size;
            }
        }
    }
    return memoryLimit;
}

当前设备的总内存空间

+ (int64_t)getTotalMemory {
    int64_t totalMemory = [[NSProcessInfo processInfo] physicalMemory];
    if (totalMemory < -1) totalMemory = -1;
    return totalMemory;
}

物理内存页面的生命周期状态

  • Free
    空闲,物理页没被任何虚拟内存使用,未使用的 RAM 容量,随时可以被应用分配使用。

  • Active
    活跃,物理页正用于一个虚拟内存页,并且最近被引用过,这种页面一般不会被交换出去

  • Inactive
    非活跃,物理页正用于一个虚拟内存页,但最近没有被引用过,这种页面有可能被交换出去

  • Speculative
    投机,对可能的内存需求做了一个猜测是的内存分配(不是活跃状态也不是非活跃状态,有可能马上被访问),对页面进行投机映射,因为很可能很快被访问到

  • Wired down
    联动,物理页正用于一个虚拟内存页,但该页面被锁定,不能交换,一般用于内核代码数据,用来存放内核代码和数据结构,它主要为内核服务,如负责网络、文件系统之类的;对于应用、framework、一些用户级别的软件是没办法分配此内存的。但是应用程序也会对 Wired Memory 的分配有所影响。

  • Purgeable
    可以理解为可释放的内存,主要是大对象或大内存块才可以使用的内存,此内存会在内存紧张的时候自动释放掉。

// 活跃的内存,正在使用或者很短时间内被使用过:已使用,但可被分页的(在iOS中,只有在磁盘上静态存在的才能被分页,例如文件的内存映射,而动态分配的内存是不能被分页的)
+ (int64_t)getActiveMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.active_count * page_size;
}
 
// 最近使用过,但是目前处于不活跃状态的内存:程序退出后却没释放的内存,以便加快再次启动,而当内存不足时,就会被回收,因此也可看作空闲内存
+ (int64_t)getInActiveMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.inactive_count * page_size;
}
 
// 用来存放内核和数据结构的内存,framework、用户级别的应用无法分配
+ (int64_t)getWiredMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.wire_count * page_size;
}

// 已使用的内存空间
+ (int64_t)getUsedMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return page_size * (vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count);
}
 
// 空闲的内存空间,:未使用的 RAM 容量,随时可以被应用分配使用。(感觉本方法和腾讯bugly崩溃统计中的可用内存大小相似)
+ (int64_t)getFreeMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.free_count * page_size;
}

// 可释放的内存空间:内存吃紧自动释放,针对大对象存放所需的大块内存空间
+ (int64_t)getPurgableMemory {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stat;
    kern_return_t kern;
    
    kern = host_page_size(host_port, &page_size);
    if (kern != KERN_SUCCESS) return -1;
    kern = host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    if (kern != KERN_SUCCESS) return -1;
    return vm_stat.purgeable_count * page_size;
}

获取当前可用内存

vm_statistics_data_t结构体中,free是空闲内存;
active是已使用,但可被分页的(在iOS中,只有在磁盘上静态存在的才能被分页,例如文件的内存映射,而动态分配的内存是不能被分页的);
inactive是不活跃的,也就是程序退出后却没释放的内存,以便加快再次启动,而当内存不足时,就会被回收,因此也可看作空闲内存;
wire就是已使用,且不可被分页的。

网上大部分的获取是这样的, 但是实测和xcode的有差别.

// 获取当前可用内存
+ (long long)getAvailableMemorySize {
    vm_statistics_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 ((vm_page_size * vmStats.free_count + vm_page_size * vmStats.inactive_count));
}

下面的这种和xcode获取的最接近, 所以更推荐用下面这个.

空闲内存看成总内存大小减去 Wired Memory大小,Active Memory大小以及Inactive Memory大小。在32位系统通过这种方式获取空闲内存与Xcode数据作比较误差范围较小,而在64位系统上的数据与Xcode数据一比较误差较大,同样找到一个逆向Xcode获取Xcode的计算内存方法。64位系统获取空闲内存的具体代码如下:

// 基于原文写法优化,把无效的计算去掉了
+ (double)usageComparisonFree {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;

    vm_size_t page_size;
    vm_statistics64_data_t vminfo;
    host_page_size(host_port, &page_size);
    host_statistics64(host_port, HOST_VM_INFO64, (host_info64_t)&vminfo,&count);

    uint64_t free_size = (vminfo.free_count + vminfo.external_page_count + vminfo.purgeable_count - vminfo.speculative_count) * page_size;
    return free_size / 1024.0 / 1024.0;
}


// 原文的写法,备份
+ (double)usageComparisonFree {
    int64_t physical_memory = [[NSProcessInfo processInfo] physicalMemory];
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
   
    vm_size_t page_size;
    vm_statistics64_data_t vminfo;
    host_page_size(host_port, &page_size);
    host_statistics64(host_port, HOST_VM_INFO64, (host_info64_t)&vminfo,&count);
    
    uint64_t total_used_count = (physical_memory /page_size) - (vminfo.free_count - vminfo.speculative_count) - vminfo.external_page_count - vminfo.purgeable_count;
    uint64_t free_size = ((physical_memory / page_size) -total_used_count) * page_size;
    return free_size / 1024.0 / 1024.0;
}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值