美文网首页
iOS 获取设备内存信息

iOS 获取设备内存信息

作者: FieryDragon | 来源:发表于2020-08-16 17:16 被阅读0次
    获取应用占用内存
    #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就是已使用,且不可被分页的。

    // 获取当前可用内存
    + (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));
    }
    
    
    Usage Comparison Free

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

    + (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 获取设备内存信息

          本文链接:https://www.haomeiwen.com/subject/kvnkxhtx.html