Recuperar de forma programática el uso de memoria en iPhone

101

Estoy tratando de recuperar la cantidad de memoria que usa mi aplicación de iPhone en cualquier momento, mediante programación. Sí, estoy al tanto de ObjectAlloc / Leaks. No estoy interesado en esos, solo para saber si es posible escribir algún código y obtener la cantidad de bytes que se utilizan e informar a través de NSLog.

Gracias.

Coocoo4Cocoa
fuente
Hombre, ya recuperé el uso de la memoria con éxito; ¿Pero puede ayudarme a responder mis preguntas relacionadas? stackoverflow.com/questions/47071265/…
Paradise
Aquí se explica cómo obtener la respuesta correcta: stackoverflow.com/a/57315975/1058199
Alex Zavatone

Respuestas:

134

Para obtener los bytes reales de memoria que usa su aplicación, puede hacer algo como el ejemplo siguiente. Sin embargo, debería familiarizarse con las diversas herramientas de creación de perfiles, ya que están diseñadas para ofrecerle una imagen mucho mejor del uso en general.

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

También hay un campo en la estructura info.virtual_size que le dará la cantidad de bytes de memoria virtual disponibles (o memoria asignada a su aplicación como memoria virtual potencial en cualquier caso). El código al que enlaza pgb le dará la cantidad de memoria disponible para el dispositivo y qué tipo de memoria es.

Jason Coco
fuente
4
gracias, exactamente lo que estaba buscando. ¿Es seguro este método en la tienda de aplicaciones?
Buju
3
Si hace Cmd + clic en task_basic_info, parece que ahora no debería usarse y reemplazarse con mach_task_basic_info. Supongo que esta versión no es compatible con la arquitectura de 64 bits, pero no estoy seguro.
cprcrack
14
En mi caso, la cantidad devuelta es más del doble de lo que emite el informe de memoria en XCode. No estoy seguro de qué hacer con eso.
Morkrom
1
¿Cómo obtener el uso de la memoria por otras aplicaciones?
Amit Khandelwal
1
@Morkrom, ¿has descubierto por qué? Tengo el mismo problema con un simulador de ejecución dos veces más grande y casi 3 veces en un dispositivo.
Julian Król
31

Los encabezados para TASK_BASIC_INFOdecir:

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

Aquí hay una versión que usa 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));
    }
}
combinacional
fuente
¿Alguna idea de por qué el valor registrado aquí es aproximadamente dos veces mayor en un simulador que los informes de Xcode y tres veces en un dispositivo real?
Julian Król
1
No sé por qué la diferencia. Eso sería una buena pregunta nueva.
combinatoria
1
Encontré la diferencia. Se debe a la memoria residente, no a los bytes en vivo
Julian Król
¿Podemos obtener el uso de memoria de otras aplicaciones? @combinatorial
Vikas Bansal
1
@VikasBansal no, no puedes.
combinatoria
18

Aquí se ha mejorado report_memory () para mostrar rápidamente el estado de la fuga en 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 nulo
fuente
2
el tamaño debe ser TASK_BASIC_INFO_COUNT en lugar de sizeof (información) - este error se copió y pegó en muchos lugares con el mismo código
Maxim Kholyavkin
18

Esto ha sido probado en Xcode 11 en Mojave 10.4.6 el 07/01/2019.

Todas las respuestas anteriores devuelven el resultado incorrecto .

A continuación, se explica cómo obtener el valor esperado escrito por Quinn de Apple, “¡El esquimal!”.

Esto usa la phys_footprintvariable de Darwin > Mach > task_infoy coincide con el valor en el indicador de memoria en el navegador de depuración de Xcode .

El valor devuelto está en bytes.

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

A continuación se muestra el código original.

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  
}  

Modificar esto ligeramente para crear un conjunto de métodos Swift a nivel de clase permite devolver fácilmente los bytes reales y la salida formateada en MB para su visualización. Utilizo esto como parte de una suite UITest automatizada para registrar la memoria utilizada antes y después de múltiples iteraciones de la misma prueba para ver si tenemos posibles fugas o asignaciones que debamos investigar.

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

¡Disfrutar!

Nota: un codificador emprendedor puede querer agregar un formateador estático a la clase para que usedMBAsString solo devuelva 2 lugares decimales significativos.

Alex Zavatone
fuente
7

Solución rápida de la respuesta 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
fuente
¿Qué hacer si queremos saber cuánto ram está usando alguna otra aplicación (skype)?
Vikas Bansal
4

Swift 3.1 (a partir del 8 de agosto de 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
fuente
1
El uso de memoria con este código muestra x3 veces el uso de memoria del depurador. ¿Por qué?
1
Bueno, supongo que necesitas dividir por (1024*1024), no por 1000000, para obtener megabytes de bytes.
ivanzoid
Eso no hace la diferencia de x3.
décadas
da un valor de memoria real, como en el depurador de Xcode, gracias
tatiana_c
2

Aquí hay una versión de 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
fuente
2
El uso de memoria con este código muestra x3 veces el uso de memoria del depurador. ¿Por qué?
incluso tengo el mismo problema para mí casi tres veces mayor que lo que se muestra en el perfil?
Sandy
-1

Versión 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
fuente
-2

A continuación se muestra la respuesta correcta:

''

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
fuente
Esto no solo devuelve el valor incorrecto, sino que llamar al método memoria "Física" significa que realmente necesita revisar su código con más frecuencia.
Alex Zavatone