Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting memory usage Live/Dirty Bytes in iOS app programmatically (not Resident/Real Bytes)

According to what I've read so far, real/resident bytes indicates the number of bytes allocated to the app including bytes that the app is no longer using but hasn't been reclaimed by the OS. And live/dirty bytes is the bytes the the app is actually using and the OS can't reclaim. I think that the number shown in XCode Debug navigator is Live Bytes.

I'm interested in getting this number programmatically (for our own statistics/analytics), but the code I found can only give the value of resident bytes, which is larger than the value that Xcode is showing on some devices (almost twice is large), actually on the same devices but different iOS versions. (on iOS 9 it gives a value almost twice as large but on iOS 11 it gives almost the same value as Xcode).

The code I am using is this:

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);
    return info.resident_size;
} else {
    NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
}

Is there some code to get the live bytes value like Xcode shows?

like image 879
Alex Itelman Avatar asked Feb 26 '18 14:02

Alex Itelman


2 Answers

I found something else, but it seems to work on the device that the previous method didn't work and not work on the device that the previous method did work :-( Now I need to figure out how to know which one to use. One device is iPhone 5s with iOS 9 and another is iPhone 5s with iOS 11. I guess I need to test on more veriaty of devices...

I found it here:

https://opensource.apple.com/source/WebKit/WebKit-7603.1.30.1.33/ios/Misc/MemoryMeasure.mm.auto.html

This translated to something like this in Objective-C:

task_vm_info_data_t vmInfo;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
kern_return_t err = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
if (err != KERN_SUCCESS)
    return 0;

NSLog(@"Memory in use vmInfo.internal (in bytes): %u", vmInfo.internal);

return vmInfo.internal;

I think if I add vmInfo.internal and vmInfo.compressed then I'll get the right result (matching what Xcode Debug navigator is showing)

It looks right for these two devices so far, and 2 other devices I tested.

So my final code looks like this:

task_vm_info_data_t info;
mach_msg_type_number_t size = TASK_VM_INFO_COUNT;
kern_return_t kerr = task_info(mach_task_self(),
                               TASK_VM_INFO,
                               (task_info_t)&info,
                               &size);
if( kerr == KERN_SUCCESS ) {
    mach_vm_size_t totalSize = info.internal + info.compressed;
    NSLog(@"Memory in use (in bytes): %u", totalSize);
    return totalSize;
} else {
    NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
}
like image 188
Alex Itelman Avatar answered Nov 04 '22 06:11

Alex Itelman


Since access to Darwin types looks slightly different in Obj-C and Swift I wanted to add the solution I came up in Swift based on Alex's answer:

let TASK_VM_INFO_COUNT = MemoryLayout<task_vm_info_data_t>.size / MemoryLayout<natural_t>.size

var vmInfo = task_vm_info_data_t()
var vmInfoSize = mach_msg_type_number_t(TASK_VM_INFO_COUNT)

let kern: kern_return_t = withUnsafeMutablePointer(to: &vmInfo) {
        $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
            task_info(mach_task_self_,
                      task_flavor_t(TASK_VM_INFO),
                      $0,
                      &vmInfoSize)
            }
        }

if kern == KERN_SUCCESS {
    let usedSize = DataSize(bytes: Int(vmInfo.internal + vmInfo.compressed))
    print("Memory in use (in bytes): %u", usedSize)
} else {
    let errorString = String(cString: mach_error_string(kern), encoding: .ascii) ?? "unknown error"
    print("Error with task_info(): %s", errorString);
}

This code is based on similar answer with mach_task_basic_info and resident_size.

like image 38
Arkadii Avatar answered Nov 04 '22 04:11

Arkadii