Recupere programaticamente o uso de memória no iPhone

101

Estou tentando recuperar a quantidade de memória que meu aplicativo para iPhone está usando a qualquer momento, de maneira programática. Sim, estou ciente sobre ObjectAlloc / Leaks. Não estou interessado neles, apenas para saber se é possível escrever algum código e obter a quantidade de bytes que está sendo utilizada e reportar via NSLog.

Obrigado.

Coocoo4Cocoa
fonte
Cara, eu já recuperei o uso da memória com sucesso; Mas você pode ajudar a responder minhas perguntas relacionadas? stackoverflow.com/questions/47071265/…
Paradise
Veja como obter a resposta correta: stackoverflow.com/a/57315975/1058199
Alex Zavatone

Respostas:

134

Para obter os bytes reais de memória que seu aplicativo está usando, você pode fazer algo como o exemplo abaixo. No entanto, você realmente deve se familiarizar com as várias ferramentas de criação de perfil, pois elas foram projetadas para lhe dar uma imagem muito melhor do uso geral.

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

Também existe um campo na estrutura info.virtual_size que fornecerá o número de bytes de memória virtual disponível (ou memória alocada para seu aplicativo como memória virtual potencial em qualquer evento). O código vinculado ao pgb fornecerá a quantidade de memória disponível para o dispositivo e que tipo de memória é.

Jason Coco
fonte
4
obrigado, exatamente o que eu estava procurando. Este método app store é seguro?
Buju
3
Se você Cmd + Click task_basic_info, parece que isso agora não deve ser usado e substituído por mach_task_basic_info. Meu palpite é que esta versão não é compatível com a arquitetura de 64 bits, mas não tenho certeza.
cprcrack
14
No meu caso, a quantidade devolvida é mais do dobro do que o relatório de memória no XCode fornece. Não tenho certeza do que fazer com isso.
Morkrom de
1
Como obter o uso de memória por outros aplicativos?
Amit Khandelwal
1
@Morkrom, você descobriu o porquê? Tenho o mesmo problema em um simulador de corrida duas vezes maior e quase 3 vezes em um dispositivo.
Julian Król
31

Os cabeçalhos para TASK_BASIC_INFOdizer:

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

Aqui está uma versão usando 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));
    }
}
combinatória
fonte
Alguma ideia de por que o valor registrado aqui é cerca de duas vezes maior em um simulador do que os relatórios do Xcode e três vezes em um dispositivo real?
Julian Król
1
Não sei por que a diferença. Isso seria uma boa pergunta nova.
combinatória de
1
Eu encontrei a diferença. É por causa da memória residente e não dos bytes vivos
Julian Król
podemos obter o uso de memória de outros aplicativos? @combinatorial
Vikas Bansal
1
@VikasBansal não, você não pode.
combinatória de
18

Aqui está report_memory () aprimorado para mostrar rapidamente o status do vazamento no 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
fonte
2
size deve ser TASK_BASIC_INFO_COUNT em vez de sizeof (info) - este erro foi copiado e colado em muitos lugares com o mesmo código
Maxim Kholyavkin
18

Isso foi testado no Xcode 11 no Mojave 10.4.6 em 01/07/2019.

Todas as respostas anteriores retornam o resultado incorreto .

Aqui está como obter o valor esperado escrito por Quinn da Apple “The Eskimo!”.

Isso usa a phys_footprintvar de Darwin > Mach > task_infoe corresponde aproximadamente ao valor no medidor de memória no navegador de depuração do Xcode .

O valor retornado está em bytes.

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

Segue o 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 isso ligeiramente para criar um conjunto de métodos Swift em nível de classe permite o fácil retorno dos bytes reais e saída formatada em MB para exibição. Eu uso isso como parte de um conjunto UITest automatizado para registrar a memória usada antes e depois de várias iterações do mesmo teste para ver se temos algum vazamento ou alocação potencial que precisamos examinar.

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

Aproveitar!

Nota: um codificador empreendedor pode querer adicionar um formatador estático à classe para que usedMBAsStringretorne apenas 2 casas decimais significativas.

Alex Zavatone
fonte
7

Solução rápida da resposta 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
fonte
o que fazer se quisermos saber quanta memória RAM algum outro aplicativo (skype) está usando?
Vikas Bansal
4

Swift 3.1 (em 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
fonte
1
O uso de memória usando este código mostra x3 vezes o uso de memória do depurador. Por quê?
1
Bem, acho que você precisa dividir por (1024*1024), não por 1000000, para obter megabytes de bytes.
ivanzoid
Isso não faz diferença de x3.
décadas de
dá um valor de memória real, como no depurador Xcode, obrigado
tatiana_c
2

Aqui está uma versão do 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
fonte
2
O uso de memória usando este código mostra x3 vezes o uso de memória do depurador. Por quê?
mesmo eu tenho o mesmo problema para mim quase três vezes maior que o que está aparecendo no perfil?
Sandy,
-1

Versão 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
fonte
-2

Abaixo está a resposta correta:

`` `

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
fonte
Isso não apenas retorna o valor incorreto, mas chamar o método de memória "Física" significa que você realmente precisa revisar seu código com mais frequência.
Alex Zavatone