112

I'm trying to retrieve the amount of memory my iPhone app is using at anytime, programmatically. Yes I'm aware about ObjectAlloc/Leaks. I'm not interested in those, only to know if it's possible to write some code and get the amount of bytes being used and report it via NSLog.

Thanks.

TheNeil
  • 3,321
  • 2
  • 27
  • 52
Coocoo4Cocoa
  • 48,756
  • 50
  • 150
  • 175

8 Answers8

137

To get the actual bytes of memory that your application is using, you can do something like the example below. However, you really should become familiar with the various profiling tools as well as they are designed to give you a much better picture of usage over-all.

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

There is also a field in the structure info.virtual_size which will give you the number of bytes available virtual memory (or memory allocated to your application as potential virtual memory in any event). The code that pgb links to will give you the amount of memory available to the device and what type of memory it is.

Jason Coco
  • 77,985
  • 20
  • 184
  • 180
  • 4
    thanks, exactly what I was searching for. Is this method app store safe? – Buju Jul 04 '11 at 13:02
  • @Jason Coco - how to call above method?? – Nitesh Meshram Dec 04 '12 at 14:46
  • @NiteshMeshram Just define the function as shown, link in, and call the function like `report_memory();`. – Jason Coco Dec 04 '12 at 18:48
  • Will this method return the correct result if there are multiple threads running within our app? – zzzzz Mar 27 '13 at 11:32
  • @iOsBoy Yes, it will trap in the kernel, so it will give you an accurate number of allocated pages at that point, but it's important to note that you shouldn't be relying on things like this. Your app should simply manage its memory wisely or use ARC. – Jason Coco Mar 28 '13 at 17:20
  • 3
    If you Cmd+Click task_basic_info, it seems that this should now not be used and replaced with mach_task_basic_info. My guess is that this version is not compatible with 64-bit architecture, but not really sure. – cprcrack Oct 30 '13 at 18:16
  • size should be TASK_BASIC_INFO_COUNT instead of sizeof(info) - this mistake copy-pasted to many places with same code – Maxim Kholyavkin Dec 02 '13 at 01:13
  • @Speakus that change gets me numbers that increase when I allocate a single small object (=good), but the increase is terabytes (=looks unrealistic ;)) – Adam Jan 13 '14 at 16:16
  • 17
    In my case, the amount returned is over twice as much as the memory report in XCode puts out. Not sure what to make of it. – Morkrom Jan 16 '14 at 19:43
  • @JasonCoco where should i have to implement this method – ganesh manoj Jul 26 '14 at 08:56
  • 1
    How to get the memory usage by other applications? – Amit Khandelwal Jul 29 '15 at 06:03
  • 2
    @Morkrom have you figured out why? I have the same problem around twice bigger running simulator and almost 3 times on a device. – Julian Oct 06 '15 at 07:56
  • I did some research about the 2x/3x bug and this seems to only affect debug builds. When running a release build on a device I saw the exact same amount of ram that Xcode shows me in debug builds. – Eike Jul 19 '16 at 18:18
  • Is there a way to do this but per object? I would like given let's say a certain UIKit object how much memory it's responsible for. @JasonCoco – zumzum Dec 02 '16 at 18:42
  • Thx man, I followed your way and succeed; but can you help answer my related questions? https://stackoverflow.com/questions/47071265/how-to-analyze-stack-info-of-a-thread – Paradise Nov 02 '17 at 09:06
  • `sizeof(info)` here is wrong. According to Quinn the Eskimo (an apple engineer) in https://forums.developer.apple.com/thread/64665, the size should be "in terms of integer_t, which is how Mach IPC counts.", so there needs to be a division by `sizeof(integer_t)` there – Mike Weller Mar 20 '18 at 12:01
  • That number is way off. Here's how to get the correct answer: https://stackoverflow.com/a/57315975/1058199 – Alex Zavatone Aug 01 '19 at 19:43
43

This has been tested on Xcode 11 in Mojave 10.4.6 on 07/01/2019, and on Xcode 11.3 as of 11/05/2020

All of the previous answers return the incorrect result.

Two Swift versions are below.

Here is how to get the expected value written by Apple's Quinn “The Eskimo!”.

This uses the phys_footprint var from Darwin > Mach > task_info and closely matches the value in the memory gauge in Xcode's Debug navigator.

The value returned is in bytes.

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

Original code follows.

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  
}  

Modifying this slightly to create a class level set of Swift methods allows easy return of the actual bytes and formatted output in MB for display. I use this as part of an automated UITest suite to log memory used before and after multiple iterations of the same test to see if we have any potential leaks or allocations we need to look into.

//  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
     }
}

Enjoy!

Note: an enterprising coder may want to add a static formatter to the class so that usedMBAsString only returns 2 significant decimal places.

Alex Zavatone
  • 4,106
  • 36
  • 54
  • 1
    This should really be the accepted answer (as an example; **only** solution for `NEPacketTunnelProvider` extension). – Top-Master Oct 14 '21 at 05:54
  • checkout this https://www.jianshu.com/p/e3161d4f3a62 work for me (这个是我试过唯一有效的, 有考虑 32 位 和 64 位设备) – Quanhua Guan Apr 27 '23 at 07:33
33

The headers forTASK_BASIC_INFO say:

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

Here is a version using 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));
    }
}
combinatorial
  • 9,132
  • 4
  • 40
  • 58
18

Here is report_memory() enhanced to rapidly show leak status in the 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;
}
sarahhodne
  • 9,796
  • 3
  • 39
  • 44
Doug Null
  • 7,989
  • 15
  • 69
  • 148
7

Swift solution of Jason Coco's answer:

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))")
    }
}
Community
  • 1
  • 1
Nazariy Vlizlo
  • 778
  • 7
  • 16
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"))
    }

}
BennyTheNerd
  • 3,930
  • 1
  • 21
  • 16
2

Here's a Swift 3 Version:

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)   
}
jeffbailey
  • 137
  • 6
1

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);
}
Jimmy KD
  • 633
  • 7
  • 14