Récupérer l'utilisation de la mémoire par programme sur l'iPhone

101

J'essaie de récupérer la quantité de mémoire utilisée par mon application iPhone à tout moment, par programmation. Oui, je connais ObjectAlloc / Leaks. Je ne suis pas intéressé par ceux-ci, seulement pour savoir s'il est possible d'écrire du code et d'obtenir la quantité d'octets utilisée et de la signaler via NSLog.

Merci.

Coocoo4Cocoa
la source
Mec, j'ai déjà récupéré l'utilisation de la mémoire avec succès; Mais pouvez-vous aider à répondre à mes questions connexes? stackoverflow.com/questions/47071265/…
Paradise
Voici comment obtenir la bonne réponse: stackoverflow.com/a/57315975/1058199
Alex Zavatone

Réponses:

134

Pour obtenir les octets réels de mémoire que votre application utilise, vous pouvez faire quelque chose comme l'exemple ci-dessous. Cependant, vous devez vraiment vous familiariser avec les différents outils de profilage et ils sont conçus pour vous donner une bien meilleure image de l'utilisation dans son ensemble.

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

Il y a aussi un champ dans la structure info.virtual_size qui vous donnera le nombre d'octets de mémoire virtuelle disponible (ou de mémoire allouée à votre application comme mémoire virtuelle potentielle dans tous les cas). Le code auquel pgb se connecte vous donnera la quantité de mémoire disponible pour l'appareil et le type de mémoire dont il s'agit.

Jason Coco
la source
4
merci, exactement ce que je cherchais. Cette méthode est-elle sûre dans l'App Store?
Buju le
3
Si vous Cmd + cliquez sur task_basic_info, il semble que cela ne devrait plus être utilisé et remplacé par mach_task_basic_info. Je suppose que cette version n'est pas compatible avec l'architecture 64 bits, mais pas vraiment sûr.
cprcrack
14
Dans mon cas, le montant retourné est deux fois supérieur à celui du rapport de mémoire dans XCode. Je ne sais pas quoi en faire.
Morkrom
1
Comment obtenir l'utilisation de la mémoire par d'autres applications?
Amit Khandelwal
1
@Morkrom avez-vous compris pourquoi? J'ai le même problème avec un simulateur de fonctionnement deux fois plus grand et presque 3 fois sur un appareil.
Julian Król
31

Les en-têtes pour TASK_BASIC_INFOdire:

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

Voici une version utilisant 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));
    }
}
combinatoire
la source
Une idée de la raison pour laquelle la valeur enregistrée ici est environ deux fois plus élevée sur un simulateur que les rapports Xcode et trois fois sur un appareil réel?
Julian Król
1
Je ne sais pas pourquoi la différence. Cela ferait une bonne nouvelle question.
combinatoire du
1
J'ai trouvé la différence. C'est à cause de la mémoire résidente et non des octets en direct
Julian Król
pouvons-nous obtenir l'utilisation de la mémoire d'autres applications? @combinatorial
Vikas Bansal
1
@VikasBansal non, vous ne pouvez pas.
combinatoire du
18

Voici report_memory () amélioré pour afficher rapidement l'état des fuites dans 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;
}
Doug Null
la source
2
la taille doit être TASK_BASIC_INFO_COUNT au lieu de sizeof (info) - cette erreur a été copiée à plusieurs endroits avec le même code
Maxim Kholyavkin
18

Cela a été testé sur Xcode 11 dans Mojave 10.4.6 le 07/01/2019.

Toutes les réponses précédentes renvoient un résultat incorrect .

Voici comment obtenir la valeur attendue écrite par Quinn d'Apple «The Eskimo!».

Cela utilise le phys_footprintvar from Darwin > Mach > task_infoet correspond étroitement à la valeur de la jauge de mémoire dans le navigateur de débogage de Xcode .

La valeur renvoyée est en octets.

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

Le code original suit.

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  
}  

Le modifier légèrement pour créer un ensemble de méthodes Swift au niveau de la classe permet de retourner facilement les octets réels et la sortie formatée en Mo pour l'affichage. J'utilise cela dans le cadre d'une suite UITest automatisée pour enregistrer la mémoire utilisée avant et après plusieurs itérations du même test pour voir si nous avons des fuites ou des allocations potentielles que nous devons examiner.

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

Prendre plaisir!

Remarque: un codeur entreprenant peut souhaiter ajouter un formateur statique à la classe afin qu'il usedMBAsStringne renvoie que 2 décimales significatives.

Alex Zavatone
la source
7

Solution rapide de la réponse de 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
la source
que faire si nous voulons savoir combien de RAM une autre application (skype) utilise?
Vikas Bansal
4

Swift 3.1 (au 8 août 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
la source
1
L'utilisation de la mémoire à l'aide de ce code montre x3 fois l'utilisation de la mémoire du débogueur. Pourquoi?
1
Eh bien, je suppose que vous devez diviser par (1024*1024), pas par 1000000, pour obtenir des mégaoctets d'octets.
ivanzoid
Cela ne fait pas la différence de x3.
décennies
cela donne une valeur de mémoire réelle, comme dans le débogueur Xcode, merci
tatiana_c
2

Voici une version 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)   
}
Jeffbailey
la source
2
L'utilisation de la mémoire à l'aide de ce code montre x3 fois l'utilisation de la mémoire du débogueur. Pourquoi?
même j'ai le même problème pour moi presque trois fois plus élevé que ce qui est affiché dans le profil?
Sandy le
-1

Version 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);
}
Jimmy Yin
la source
-2

Voici la bonne réponse:

''

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
la source
Non seulement cela renvoie une valeur incorrecte, mais l'appel de la méthode "Physics" memory signifie que vous devez vraiment revoir votre code plus souvent.
Alex Zavatone