【问题标题】:Programmatically retrieve memory usage on iPhone以编程方式检索 iPhone 上的内存使用情况
【发布时间】:2010-10-21 16:35:24
【问题描述】:

我正在尝试以编程方式随时检索我的 iPhone 应用程序正在使用的内存量。是的,我知道 ObjectAlloc/Leaks。我对这些不感兴趣,只是想知道是否可以编写一些代码并获取正在使用的字节数并通过 NSLog 报告。

谢谢。

【问题讨论】:

    标签: ios objective-c iphone cocoa-touch memory


    【解决方案1】:

    要获取您的应用程序正在使用的实际内存字节数,您可以执行以下示例中的操作。但是,您确实应该熟悉各种分析工具,因为它们旨在让您更好地了解总体使用情况。

    #import <mach/mach.h>
    
    // ...
    
    void report_memory(void) {
      struct task_basic_info info;
      mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT;
      kern_return_t kerr = task_info(mach_task_self(),
                                     TASK_BASIC_INFO,
                                     (task_info_t)&info,
                                     &size);
      if( kerr == KERN_SUCCESS ) {
        NSLog(@"Memory in use (in bytes): %lu", info.resident_size);
        NSLog(@"Memory in use (in MiB): %f", ((CGFloat)info.resident_size / 1048576));
      } else {
        NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
      }
    }
    

    在结构 info.virtual_size 中还有一个字段,它将为您提供可用虚拟内存的字节数(或分配给您的应用程序的内存作为潜在的虚拟内存在任何情况下)。 pgb 链接到的代码将为您提供设备可用的内存量以及它是什么类型的内存。

    【讨论】:

    • 谢谢,正是我正在寻找的。这种方法应用商店安全吗?
    • 如果你 Cmd+单击 task_basic_info,现在似乎不应该使用它并用 mach_task_basic_info 替换。我的猜测是这个版本不兼容 64 位架构,但不太确定。
    • 在我的例子中,返回的数量是 XCode 中内存报告的两倍多。不知道该怎么做。
    • 如何获取其他应用的内存使用情况?
    • @Morkrom 你知道为什么吗?我在运行模拟器的两倍和在设备上几乎 3 倍时遇到了同样的问题。
    【解决方案2】:

    这里的 report_memory() 得到了增强,可以在 NSLog() 中快速显示泄漏状态。

    void report_memory(void) {
        static unsigned last_resident_size=0;
        static unsigned greatest = 0;
        static unsigned last_greatest = 0;
    
        struct task_basic_info info;
        mach_msg_type_number_t size = sizeof(info);
        kern_return_t kerr = task_info(mach_task_self(),
                                   TASK_BASIC_INFO,
                                   (task_info_t)&info,
                                   &size);
        if( kerr == KERN_SUCCESS ) {
            int diff = (int)info.resident_size - (int)last_resident_size;
            unsigned latest = info.resident_size;
            if( latest > greatest   )   greatest = latest;  // track greatest mem usage
            int greatest_diff = greatest - last_greatest;
            int latest_greatest_diff = latest - greatest;
            NSLog(@"Mem: %10u (%10d) : %10d :   greatest: %10u (%d)", info.resident_size, diff,
              latest_greatest_diff,
              greatest, greatest_diff  );
        } else {
            NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
        }
        last_resident_size = info.resident_size;
        last_greatest = greatest;
    }
    

    【讨论】:

    • size 应该是 TASK_BASIC_INFO_COUNT 而不是 sizeof(info) - 这个错误用相同的代码复制粘贴到许多地方
    【解决方案3】:

    TASK_BASIC_INFO 的标头说:

    /* Don't use this, use MACH_TASK_BASIC_INFO instead */
    

    这是一个使用MACH_TASK_BASIC_INFO的版本:

    void report_memory(void)
    {
        struct mach_task_basic_info info;
        mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT;
        kern_return_t kerr = task_info(mach_task_self(),
                                       MACH_TASK_BASIC_INFO,
                                       (task_info_t)&info,
                                       &size);
        if( kerr == KERN_SUCCESS ) {
            NSLog(@"Memory in use (in bytes): %u", info.resident_size);
        } else {
            NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
        }
    }
    

    【讨论】:

    • 知道为什么这里记录的值在模拟器上大约是 Xcode 报告的两倍,在真实设备上是三倍?
    • 我不知道为什么会有差异。这将是一个很好的新问题。
    • 我发现了不同之处。这是因为常驻内存而不是活动字节
    • 我们可以获取其他应用程序的内存使用情况吗? @combinatorial
    • @VikasBansal 不,你不能。
    【解决方案4】:

    Jason Coco的快速解决方案@的回答:

    func reportMemory() {
        let name = mach_task_self_
        let flavor = task_flavor_t(TASK_BASIC_INFO)
        let basicInfo = task_basic_info()
        var size: mach_msg_type_number_t = mach_msg_type_number_t(sizeofValue(basicInfo))
        let pointerOfBasicInfo = UnsafeMutablePointer<task_basic_info>.alloc(1)
    
        let kerr: kern_return_t = task_info(name, flavor, UnsafeMutablePointer(pointerOfBasicInfo), &size)
        let info = pointerOfBasicInfo.move()
        pointerOfBasicInfo.dealloc(1)
    
        if kerr == KERN_SUCCESS {
            print("Memory in use (in bytes): \(info.resident_size)")
        } else {
            print("error with task info(): \(mach_error_string(kerr))")
        }
    }
    

    【讨论】:

    • 如果我们想知道其他应用程序(Skype)正在使用多少内存该怎么办?
    【解决方案5】:

    这是一个 Swift 3 版本:

    func mach_task_self() -> task_t {
        return mach_task_self_
    }
    
    func getMegabytesUsed() -> Float? {
        var info = mach_task_basic_info()
        var count = mach_msg_type_number_t(MemoryLayout.size(ofValue: info) / MemoryLayout<integer_t>.size)
        let kerr = withUnsafeMutablePointer(to: &info) { infoPtr in
            return infoPtr.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { (machPtr: UnsafeMutablePointer<integer_t>) in
                return task_info(
                    mach_task_self(),
                    task_flavor_t(MACH_TASK_BASIC_INFO),
                    machPtr,
                    &count
                )
            }
        }
        guard kerr == KERN_SUCCESS else {
            return nil
        }  
        return Float(info.resident_size) / (1024 * 1024)   
    }
    

    【讨论】:

    • 使用此代码的内存使用量显示了调试器内存使用量的 x3 倍。为什么?
    • 即使我遇到同样的问题几乎是个人资料中显示的问题的三倍?
    【解决方案6】:

    Swift 3.1 (截至 2017 年 8 月 8 日)

    func getMemory() {
    
        var taskInfo = mach_task_basic_info()
        var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4
        let kerr: kern_return_t = withUnsafeMutablePointer(to: &taskInfo) {
            $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
                task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
            }
        }
        if kerr == KERN_SUCCESS {
            let usedMegabytes = taskInfo.resident_size/(1024*1024)
            print("used megabytes: \(usedMegabytes)")
        } else {
            print("Error with task_info(): " +
                (String(cString: mach_error_string(kerr), encoding: String.Encoding.ascii) ?? "unknown error"))
        }
    
    }
    

    【讨论】:

    • 使用此代码的内存使用量显示了调试器内存使用量的 x3 倍。为什么?
    • 好吧,我猜你需要除以(1024*1024),而不是1000000,才能从字节中获得兆字节。
    • 这对 x3 没有影响。
    • 它给出了一个真实的内存值,就像在 Xcode 调试器中一样,谢谢
    【解决方案7】:

    这已于 2019 年 7 月 1 日在 Mojave 10.4.6 中的 Xcode 11 和 2020 年 11 月 5 日的 Xcode 11.3 上进行了测试

    之前的所有答案都返回不正确的结果

    下面有两个Swift 版本。

    这里是如何获得 Apple 的 Quinn “The Eskimo!”所写的期望值。

    这使用了来自 Darwin &gt; Mach &gt; task_infophys_footprint 变量,并且与 Xcode 调试导航器中的内存量表中的值非常匹配

    返回的值以字节为单位。

    https://forums.developer.apple.com/thread/105088#357415

    原始代码如下。

    func memoryFootprint() -> mach_vm_size_t? {  
        // The `TASK_VM_INFO_COUNT` and `TASK_VM_INFO_REV1_COUNT` macros are too  
        // complex for the Swift C importer, so we have to define them ourselves.  
        let TASK_VM_INFO_COUNT = mach_msg_type_number_t(MemoryLayout<task_vm_info_data_t>.size / MemoryLayout<integer_t>.size)  
        let TASK_VM_INFO_REV1_COUNT = mach_msg_type_number_t(MemoryLayout.offset(of: \task_vm_info_data_t.min_address)! / MemoryLayout<integer_t>.size)  
        var info = task_vm_info_data_t()  
        var count = TASK_VM_INFO_COUNT  
        let kr = withUnsafeMutablePointer(to: &info) { infoPtr in  
            infoPtr.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { intPtr in  
                task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), intPtr, &count)  
            }  
        }  
        guard  
            kr == KERN_SUCCESS,  
            count >= TASK_VM_INFO_REV1_COUNT  
        else { return nil }  
        return info.phys_footprint  
    }  
    

    稍微修改一下以创建类级别的 Swift 方法集,可以轻松返回实际字节和格式化输出(以 MB 为单位)以供显示。我将其用作自动化 UITest 套件的一部分,以记录同一测试的多次迭代前后使用的内存,以查看是否有任何潜在的泄漏或分配需要调查。

    //  Created by Alex Zavatone on 8/1/19.
    //
    
    class Memory: NSObject {
    
        // From Quinn the Eskimo at Apple.
        // https://forums.developer.apple.com/thread/105088#357415
    
        class func memoryFootprint() -> Float? {
            // The `TASK_VM_INFO_COUNT` and `TASK_VM_INFO_REV1_COUNT` macros are too
            // complex for the Swift C importer, so we have to define them ourselves.
            let TASK_VM_INFO_COUNT = mach_msg_type_number_t(MemoryLayout<task_vm_info_data_t>.size / MemoryLayout<integer_t>.size)
            let TASK_VM_INFO_REV1_COUNT = mach_msg_type_number_t(MemoryLayout.offset(of: \task_vm_info_data_t.min_address)! / MemoryLayout<integer_t>.size)
            var info = task_vm_info_data_t()
            var count = TASK_VM_INFO_COUNT
            let kr = withUnsafeMutablePointer(to: &info) { infoPtr in
                infoPtr.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { intPtr in
                    task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), intPtr, &count)
                }
            }
            guard
                kr == KERN_SUCCESS,
                count >= TASK_VM_INFO_REV1_COUNT
                else { return nil }
            
            let usedBytes = Float(info.phys_footprint)
            return usedBytes
        }
        
        class func formattedMemoryFootprint() -> String
        {
            let usedBytes: UInt64? = UInt64(self.memoryFootprint() ?? 0)
            let usedMB = Double(usedBytes ?? 0) / 1024 / 1024
            let usedMBAsString: String = "\(usedMB)MB"
            return usedMBAsString
         }
    }
    

    享受吧!

    注意:有进取心的编码员可能希望向类添加静态格式化程序,以便usedMBAsString 仅返回 2 个有效小数位。

    【讨论】:

    • 这确实应该是公认的答案(例如;only 解决方案适用于NEPacketTunnelProvider 扩展)。
    【解决方案8】:

    Objective-C version:

    size_t memoryFootprint()
    {
        task_vm_info_data_t vmInfo;
        mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
        kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
        if (result != KERN_SUCCESS)
            return 0;
        return static_cast<size_t>(vmInfo.phys_footprint);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-05
      • 2013-08-29
      • 1970-01-01
      • 1970-01-01
      • 2017-09-01
      • 2020-07-30
      相关资源
      最近更新 更多