Programmgesteuertes Abrufen der Speichernutzung auf dem iPhone

101

Ich versuche, die von meiner iPhone-App verwendete Speichermenge jederzeit programmgesteuert abzurufen. Ja, mir sind ObjectAlloc / Leaks bekannt. Ich bin nicht an diesen interessiert, nur um zu wissen, ob es möglich ist, Code zu schreiben und die Anzahl der verwendeten Bytes zu ermitteln und über NSLog zu melden.

Vielen Dank.

Coocoo4Cocoa
quelle
Mann, ich rufe die Speichernutzung bereits erfolgreich ab; Aber können Sie mir bei der Beantwortung meiner Fragen helfen? stackoverflow.com/questions/47071265/…
Paradise
So erhalten Sie die richtige Antwort: stackoverflow.com/a/57315975/1058199
Alex Zavatone

Antworten:

134

Um die tatsächlichen Speicherbytes abzurufen, die Ihre Anwendung verwendet, können Sie das folgende Beispiel ausführen. Sie sollten sich jedoch unbedingt mit den verschiedenen Profiling-Tools vertraut machen, da diese Ihnen ein viel besseres Bild der Nutzung insgesamt vermitteln sollen.

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

Es gibt auch ein Feld in der Struktur info.virtual_size, das Ihnen die Anzahl der verfügbaren virtuellen Bytes angibt (oder den Ihrer Anwendung in jedem Fall als potenzieller virtueller Speicher zugewiesenen Speicher). Der Code, mit dem pgb verknüpft ist, gibt an, wie viel Speicher für das Gerät verfügbar ist und um welche Art von Speicher es sich handelt.

Jason Coco
quelle
4
danke, genau das, wonach ich gesucht habe. Ist diese Methode App Store sicher?
Buju
3
Wenn Sie Cmd + Click task_basic_info, scheint es, dass dies jetzt nicht verwendet und durch mach_task_basic_info ersetzt werden sollte. Ich vermute, dass diese Version nicht mit der 64-Bit-Architektur kompatibel ist, aber nicht wirklich sicher.
Cprcrack
14
In meinem Fall ist der zurückgegebene Betrag mehr als doppelt so hoch wie der Speicherbericht in XCode. Ich bin mir nicht sicher, was ich davon halten soll.
Morkrom
1
Wie erhalte ich die Speichernutzung durch andere Anwendungen?
Amit Khandelwal
1
@Morkrom hast du herausgefunden warum? Ich habe das gleiche Problem mit einem doppelt so großen laufenden Simulator und fast dreimal mit einem Gerät.
Julian Król
31

Die Überschriften TASK_BASIC_INFOsagen:

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

Hier ist eine Version mit 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));
    }
}
kombinatorisch
quelle
Gibt es eine Idee, warum der hier protokollierte Wert auf einem Simulator etwa doppelt so groß ist wie auf Xcode-Berichten und auf einem realen Gerät dreimal so groß?
Julian Król
1
Ich weiß nicht warum der Unterschied. Das wäre eine gute neue Frage.
kombinatorisch
1
Ich habe den Unterschied gefunden. Es ist wegen des residenten Gedächtnisses nicht die Live-Bytes
Julian Król
Können wir die Speichernutzung anderer Anwendungen erhalten? @combinatorial
Vikas Bansal
1
@ VikasBansal nein du kannst nicht.
kombinatorisch
18

Hier ist report_memory () erweitert, um den Leckstatus im NSLog () schnell anzuzeigen.

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;
}
Doug Null
quelle
2
Größe sollte TASK_BASIC_INFO_COUNT anstelle von sizeof (info) sein - dieser Fehler wurde an vielen Stellen mit demselben Code
kopiert
18

Dies wurde auf Xcode 11 in Mojave 10.4.6 am 07/01/2019 getestet.

Alle vorherigen Antworten geben das falsche Ergebnis zurück .

Hier erfahren Sie, wie Sie den erwarteten Wert von Apples Quinn "The Eskimo!"

Dies verwendet die Variablephys_footprint von Darwin > Mach > task_infound stimmt eng mit dem Wert in der Speicheranzeige im Debug-Navigator von Xcode überein .

Der zurückgegebene Wert wird in Byte angegeben.

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

Der Originalcode folgt.

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  
}  

Wenn Sie dies geringfügig ändern, um einen Satz von Swift-Methoden auf Klassenebene zu erstellen, können Sie die tatsächlichen Bytes und die formatierte Ausgabe in MB einfach zur Anzeige zurückgeben. Ich verwende dies als Teil einer automatisierten UITest-Suite, um den vor und nach mehreren Iterationen desselben Tests verwendeten Speicher zu protokollieren, um festzustellen, ob wir potenzielle Lecks oder Zuordnungen haben, die wir untersuchen müssen.

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

Genießen!

Hinweis: Ein unternehmungslustiger Codierer möchte der Klasse möglicherweise einen statischen Formatierer hinzufügen, sodass usedMBAsStringnur 2 signifikante Dezimalstellen zurückgegeben werden.

Alex Zavatone
quelle
7

Schnelle Lösung der Antwort von 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))")
    }
}
Nazariy Vlizlo
quelle
Was tun, wenn wir wissen möchten, wie viel RAM eine andere Anwendung (Skype) verwendet?
Vikas Bansal
4

Swift 3.1 (Stand 8. August 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
quelle
1
Die Speichernutzung mit diesem Code zeigt das 3-fache der Speichernutzung des Debuggers. Warum?
1
Nun, ich denke, Sie müssen durch (1024*1024)und nicht durch dividieren 1000000, um Megabyte aus Bytes zu erhalten.
Ivanzoid
Das macht keinen Unterschied zu x3.
Jahrzehnte
es gibt einen echten Speicherwert, wie im Xcode-Debugger, danke
tatiana_c
2

Hier ist eine 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
quelle
2
Die Speichernutzung mit diesem Code zeigt das 3-fache der Speichernutzung des Debuggers. Warum?
Sogar ich habe das gleiche Problem für mich fast dreimal höher als das, was im Profil angezeigt wird.
Sandy
-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 Yin
quelle
-2

Unten ist die richtige Antwort:

`` `

float GetTotalPhysicsMemory()
{
    struct task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kr;
    kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
    if (kr == KERN_SUCCESS) 
        return (float)(info.resident_size) / 1024.0 / 1024.0;
    else
        return 0;
}

`` `

rensq
quelle
Dies gibt nicht nur den falschen Wert zurück. Wenn Sie die Methode "Physik" -Speicher aufrufen, müssen Sie Ihren Code wirklich häufiger überprüfen.
Alex Zavatone