以编程方式在iPhone上获取内存使用情况

113

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

谢谢。

8个回答

137

要获取你的应用程序实际使用的内存字节,你可以像下面的示例一样进行操作。然而,你真的应该熟悉各种分析工具,因为它们旨在为你提供更好的整体使用情况图像。

#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链接到的代码将为您提供设备可用的内存量以及内存类型。


4
谢谢,这正是我在寻找的。这种方法是否安全可靠,可以在应用商店中使用吗? - Buju
3
如果你使用Cmd+点击task_basic_info,似乎现在应该停止使用它,并用mach_task_basic_info替换。我猜测这个版本不兼容64位架构,但并不确定。 - cprcrack
17
就我的情况而言,返回的金额是 XCode 中所报告的内存使用量的两倍以上。不确定如何解释这种情况。 - Morkrom
1
如何获取其他应用程序的内存使用情况? - Amit Khandelwal
2
@Morkrom,你弄清楚原因了吗?我在运行模拟器时遇到了同样的问题,而在设备上则大约增加了两倍的大小,几乎是三倍。 - Julian
显示剩余12条评论

45

这已在2019年7月1日在Mojave 10.4.6上使用Xcode 11进行了测试,并在2020年11月5日作为Xcode 11.3的一部分进行了测试。

所有之前的答案都返回错误的结果

下面是两个Swift版本。

以下是如何获得由苹果公司的Quinn “The Eskimo!”编写的预期值。

这使用了从Darwin > Mach > task_info中的phys_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位有效小数。


1
这真的应该成为被接受的答案(作为一个例子;NEPacketTunnelProvider扩展的唯一解决方案)。 - Top-Master
1
看看这个链接 https://www.jianshu.com/p/e3161d4f3a62 对我很有用(这是我试过唯一有效的,考虑了32位和64位设备)。 - Quanhua Guan

33

TASK_BASIC_INFO的头文件如下:

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

这里是使用版本的代码:
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报告的两倍,在真实设备上则是三倍? - Julian
1
我不知道为什么会有这种差异。这可以成为一个很好的新问题。 - combinatorial
1
我找到了区别所在。这是由于常驻内存而不是活动字节。 - Julian
1
@VikasBansal 不可以。 - combinatorial
@combinatorial 那么活动监视器是如何做到的呢? - Vikas Bansal
显示剩余3条评论

18

这里是 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;
}

4
应该使用 TASK_BASIC_INFO_COUNT 作为大小而不是 sizeof(info) - 这个错误已经复制粘贴到许多具有相同代码的地方。 - Maxim Kholyavkin

7

Jason Coco的答案的Swift解决方案:

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)正在使用多少RAM,该怎么办? - Vikas Bansal

4

Swift 3.1 (As of August 8, 2017)

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"))
    }

}

1
使用此代码的内存使用情况显示比调试器中的内存使用情况高出3倍。为什么? - user3407174
1
嗯,我想你需要除以(1024*1024)而不是1000000,才能从字节得到兆字节。 - ivanzoid
那不会使x3的差异。 - decades
它会给出实际的内存值,就像Xcode调试器一样,谢谢。 - tatiana_c

2

以下是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)   
}

2
使用此代码的内存使用情况显示比调试器中的内存使用情况高出3倍。为什么? - user3407174
即使我遇到了与我的个人资料显示相比几乎高出三倍的相同问题? - Sanoj Kashyap

1

Objective-C版本:

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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接