Recupera in modo programmatico l'utilizzo della memoria su iPhone


101

Sto cercando di recuperare la quantità di memoria utilizzata dalla mia app per iPhone in qualsiasi momento, a livello di programmazione. Sì, sono a conoscenza di ObjectAlloc / Leaks. Non mi interessano, solo per sapere se è possibile scrivere del codice e ottenere la quantità di byte utilizzati e segnalarlo tramite NSLog.

Grazie.


Amico, ho già recuperato con successo l'utilizzo della memoria; Ma puoi aiutare a rispondere alle mie domande correlate? stackoverflow.com/questions/47071265/…
Paradise

Ecco come ottenere la risposta corretta: stackoverflow.com/a/57315975/1058199
Alex Zavatone

Risposte:


134

Per ottenere i byte effettivi di memoria che la tua applicazione sta utilizzando, puoi fare qualcosa come nell'esempio seguente. Tuttavia, dovresti davvero acquisire familiarità con i vari strumenti di profilazione, poiché sono progettati per darti un'immagine molto migliore dell'utilizzo nel complesso.

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

C'è anche un campo nella struttura info.virtual_size che ti darà il numero di byte di memoria virtuale disponibile (o memoria allocata alla tua applicazione come potenziale memoria virtuale in ogni caso). Il codice a cui pgb si collega fornirà la quantità di memoria disponibile per il dispositivo e il tipo di memoria che è.


4
grazie, esattamente quello che stavo cercando. Questo metodo di app store è sicuro?
Buju

3
Se Cmd + clic su task_basic_info, sembra che questo ora non dovrebbe essere utilizzato e sostituito con mach_task_basic_info. La mia ipotesi è che questa versione non sia compatibile con l'architettura a 64 bit, ma non ne sono sicuro.
cprcrack

14
Nel mio caso, l'importo restituito è più del doppio di quanto emesso dal report di memoria in XCode. Non sono sicuro di cosa farne.
Mork dal

1
Come ottenere l'utilizzo della memoria da altre applicazioni?
Amit Khandelwal

1
@Morkrom hai capito perché? Ho lo stesso problema con un simulatore di corsa due volte più grande e quasi 3 volte su un dispositivo.
Julian Król

31

Le intestazioni per TASK_BASIC_INFOdire:

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

Ecco una versione che utilizza 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));
    }
}

Qualche idea sul perché il valore registrato qui sia circa due volte più grande su un simulatore rispetto ai rapporti Xcode e tre volte su un dispositivo reale?
Julian Król

1
Non so perché la differenza. Sarebbe una buona nuova domanda.
combinatoria

1
Ho trovato la differenza. È a causa della memoria residente, non dei byte in tempo reale
Julian Król

possiamo ottenere l'utilizzo della memoria di altre applicazioni? @combinatorial
Vikas Bansal

1
@VikasBansal no non puoi.
combinatoria

18

Ecco report_memory () migliorato per mostrare rapidamente lo stato della perdita in 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;
}

2
la dimensione dovrebbe essere TASK_BASIC_INFO_COUNT invece di sizeof (info) - questo errore è stato copiato in molti posti con lo stesso codice
Maxim Kholyavkin

18

Questo è stato testato su Xcode 11 in Mojave 10.4.6 il 07/01/2019.

Tutte le risposte precedenti restituiscono il risultato errato .

Ecco come ottenere il valore atteso scritto da Quinn di Apple "The Eskimo!".

Questo utilizza la phys_footprintvariabile da Darwin > Mach > task_infoe corrisponde strettamente al valore nell'indicatore di memoria nel navigatore di debug di Xcode .

Il valore restituito è in byte.

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

Segue il codice originale.

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  
}  

Modificandolo leggermente per creare un set a livello di classe di metodi Swift, è possibile restituire facilmente i byte effettivi e l'output formattato in MB per la visualizzazione. Lo uso come parte di una suite UITest automatizzata per registrare la memoria utilizzata prima e dopo più iterazioni dello stesso test per vedere se abbiamo potenziali perdite o allocazioni che dobbiamo esaminare.

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

Godere!

Nota: un programmatore intraprendente potrebbe voler aggiungere un formattatore statico alla classe in modo che usedMBAsStringrestituisca solo 2 cifre decimali significative.


7

Soluzione rapida della risposta di 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))")
    }
}

cosa fare se vogliamo sapere quanta RAM sta usando qualche altra applicazione (skype)?
Vikas Bansal

4

Swift 3.1 (all'8 agosto 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
L'utilizzo della memoria utilizzando questo codice mostra x3 volte l'utilizzo della memoria dal debugger. Perché?

1
Beh, immagino che tu debba dividere per (1024*1024), non per 1000000, per ottenere megabyte da byte.
ivanzoid

Questo non fa la differenza di x3.
decadi

fornisce un valore di memoria reale, come nel debugger Xcode, grazie
tatiana_c

2

Ecco una versione di 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
L'utilizzo della memoria utilizzando questo codice mostra x3 volte l'utilizzo della memoria dal debugger. Perché?

anche io ho lo stesso problema per me quasi tre volte superiore a quello che mostra di profilo?
Sandy

-1

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

-2

Di seguito è la risposta corretta:

`` `

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

`` `


Non solo questo restituisce il valore errato, chiamare il metodo "Physics" memory significa che hai davvero bisogno di rivedere il tuo codice più spesso.
Alex Zavatone
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.