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

Posted 想名真难

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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分配的内存少。
但是本人在项目中更倾向于使用第二种获取方式

系统总内存空间

+ (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;

以上是关于iOS 获取设备占用内存,剩余内存的主要内容,如果未能解决你的问题,请参考以下文章

kettle内存占用怎么解决?

获取进程占用的内存

使用adb查看CPU和内存

Linux服务器如何查看CPU占用率、内存占用、带宽占用

iOS uiimage内存占用大小计算

用vc++,如何读出设备信息,入cpu,内存,分区,各个分区的剩余空间等?