以编程方式检索 iPhone 上的内存使用情况

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

谢谢。

60829 次浏览

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.

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

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

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

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

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


}

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.

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