budget de mémoire maximal de l'application iOS

152

Je travaille sur un jeu iOS qui cible au minimum les 3gs. Nous utilisons des ressources HD pour les périphériques d'affichage Retina (iPhone 4, iPod touch 4e génération).

En ce qui concerne la mémoire, l'Ipod Touch 4e génération semble être l'appareil le plus contraignant pour nous car il dispose de la même quantité de RAM (256 par rapport aux 512 de l'iPhone 4) que 3gs, mais nous utilisons des actifs HD dessus. L'application plantait en essayant de charger 100 à 110 Mo de RAM, mais maintenant que nous sommes à 70 Mo, nous n'avons jamais eu de crash de chargement.

Après de nombreuses recherches, il ne semble pas y avoir de limite stricte officielle, alors comment devrions-nous savoir quel budget mémoire utiliser pour être en sécurité? Nous voulons pouvoir donner aux artistes un budget qu'ils peuvent utiliser sans soucis de mémoire pour chaque carte.

frilla
la source
14
Je ne sais pas comment cette question peut être une copie de quelque chose qui a été demandé plus tard.
Jasper

Réponses:

42

Je pense que vous avez répondu à votre propre question: essayez de ne pas dépasser la limite de 70 Mo, mais cela dépend vraiment de beaucoup de choses: quelle version d'iOS vous utilisez (pas SDK), combien d'applications exécutées en arrière-plan, quelle mémoire exacte vous utilisez etc.

Évitez simplement les éclaboussures de mémoire instantanées (par exemple, vous utilisez 40 Mo de RAM, puis allouez 80 Mo de plus pour un calcul court). Dans ce cas, iOS tuerait votre application immédiatement.

Vous devriez également envisager le chargement différé des ressources (ne les chargez que lorsque vous en avez vraiment besoin et pas à l'avance).

Max
la source
2
C'est juste que nous voulions mettre autant de choses que possible (graphismes et sons). Les artistes voudront toujours investir autant qu'ils le peuvent dans un jeu, c'est pourquoi je veux les limiter avec un budget. Je suppose que nous devrons simplement tester sur de nombreux appareils différents dans différents paramètres pour trouver une empreinte mémoire maximale raisonnable à utiliser.
frilla
2
L'allocation de seulement 70 Mo (ce qui est probablement sous le budget) à tout moment sur cet appareil (même après une utilisation intensive dans d'autres applications gourmandes en mémoire) garantira toujours une allocation réussie, ou est-ce que cela plantera encore?
Steven Lu
1
@Steven Lu cela dépend de votre appareil. Par exemple, sur les plus récents, comme l'iPhone5 ou l'iPad4, l'allocation de 70 Mo n'est pas du tout un problème.
Max
1
oui mais je veux savoir si je peux être sûr que tant que je garde l'utilisation totale de mon application sous le budget de mémoire spécifique de l'appareil magique, elle ne sera pas interrompue!
Steven Lu le
1
il n'y a aucune garantie
Max
421

Résultats des tests avec l'utilitaire Split a écrit (le lien est dans sa réponse):

appareil: (montant de l'accident / montant total / pourcentage du total)

  • iPad1: 127 Mo / 256 Mo / 49%
  • iPad2: 275 Mo / 512 Mo / 53%
  • iPad3: 645 Mo / 1024 Mo / 62%
  • iPad4: 585 Mo / 1024 Mo / 57% (iOS 8.1)
  • iPad Mini 1ère génération: 297 Mo / 512 Mo / 58%
  • iPad Mini rétine: 696 Mo / 1024 Mo / 68% (iOS 7.1)
  • iPad Air: 697 Mo / 1024 Mo / 68%
  • iPad Air 2: 1383 Mo / 2048 Mo / 68% (iOS 10.2.1)
  • iPad Pro 9,7 ": 1395 Mo / 1971 Mo / 71% (iOS 10.0.2 (14A456))
  • iPad Pro 10,5 ": 3057/4000/76% (iOS 11 beta4)
  • iPad Pro 12,9 pouces (2015): 3058/3999/76% (iOS 11.2.1)
  • iPad Pro 12,9 pouces (2017): 3057/3974/77% (iOS 11 beta4)
  • iPad Pro 11,0 pouces (2018): 2858/3769/76% (iOS 12.1)
  • iPad Pro 12,9 po (2018, 1 To): 4598/5650/81% (iOS 12.1)
  • iPad 10.2: 1844/2998/62% (iOS 13.2.3)
  • iPod touch 4e génération: 130 Mo / 256 Mo / 51% (iOS 6.1.1)
  • iPod touch 5e génération: 286 Mo / 512 Mo / 56% (iOS 7.0)
  • iPhone4: 325 Mo / 512 Mo / 63%
  • iPhone4s: 286 Mo / 512 Mo / 56%
  • iPhone5: 645 Mo / 1024 Mo / 62%
  • iPhone5s: 646 Mo / 1024 Mo / 63%
  • iPhone6: 645 Mo / 1024 Mo / 62% (iOS 8.x)
  • iPhone6 ​​+: 645 Mo / 1024 Mo / 62% (iOS 8.x)
  • iPhone6s: 1396 Mo / 2048 Mo / 68% (iOS 9.2)
  • iPhone6s +: 1392 Mo / 2048 Mo / 68% (iOS 10.2.1)
  • iPhoneSE: 1395 Mo / 2048 Mo / 69% (iOS 9.3)
  • iPhone7: 1395/2048 Mo / 68% (iOS 10.2)
  • iPhone7 +: 2040 Mo / 3072 Mo / 66% (iOS 10.2.1)
  • iPhone8: 1364/1990 Mo / 70% (iOS 12.1)
  • iPhone X: 1392/2785/50% (iOS 11.2.1)
  • iPhone XS: 2040/3754/54% (iOS 12.1)
  • iPhone XS Max: 2039/3735/55% (iOS 12.1)
  • iPhone XR: 1792/2813/63% (iOS 12.1)
  • iPhone 11: 2068/3844/54% (iOS 13.1.3)
  • iPhone 11 Pro Max: 2067/3740/55% (iOS 13.2.3)
Jaspe
la source
2
iPhone4: valeur similaire confirmée, semble légitime: P
cprcrack
3
L'iPhone 5 plante à ± 645 Mo.
asp_net
4
@JasperPol J'ai édité votre message pour inclure divers appareils que j'ai, j'espère que ça va. J'ai ajouté la version iOS sur laquelle j'ai testé au cas où cela importerait, mais n'hésitez pas à la supprimer si vous pensez que ce n'est pas important.
JosephH
2
Génial que cette liste ait été créée et maintenue. D'après mon expérience, j'ai dû garder la mémoire beaucoup plus basse pour être en sécurité, peut-être 20% de ce qui est montré ici. Les différences d'appareil à appareil sont également très variables.
user1021430
1
Je viens de l'exécuter sur un iPad Pro 12.9. Avertissement de mémoire à 2451 Mo, plantage à 3064 Mo, total de 3981 Mo.
écluse le
134

J'ai créé un petit utilitaire qui essaie d'allouer autant de mémoire que possible pour planter et il enregistre quand des avertissements de mémoire et un crash se sont produits. Cela permet de savoir quel est le budget mémoire de tout appareil iOS.

https://github.com/Split82/iOSMemoryBudgetTest

Divisé
la source
J'ai fait un test intéressant: j'ai exécuté mon application avec l'utilisation de la mémoire de surveillance xcode, entré en arrière-plan, exécuté le BudgetTest. Le test a été tué alors que mon application en arrière-plan ne l'était pas. Je veux savoir pourquoi. De plus, cela va à l'encontre de ce que @cprcrack a dit dans l'autre réponse.
Roberto
19

Dans mon application, l'expérience utilisateur est meilleure si plus de mémoire est utilisée, je dois donc décider si je dois vraiment libérer toute la mémoire que je peux didReceiveMemoryWarning. Sur la base de la réponse de Split et Jasper Pol, l'utilisation d'un maximum de 45% de la mémoire totale de l'appareil semble être un seuil sûr (merci les gars).

Au cas où quelqu'un voudrait examiner ma mise en œuvre réelle:

#import "mach/mach.h"

- (void)didReceiveMemoryWarning
{
    // Remember to call super
    [super didReceiveMemoryWarning];

    // If we are using more than 45% of the memory, free even important resources,
    // because the app might be killed by the OS if we don't
    if ([self __getMemoryUsedPer1] > 0.45)
    {
        // Free important resources here
    }

    // Free regular unimportant resources always here
}

- (float)__getMemoryUsedPer1
{
    struct mach_task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
    if (kerr == KERN_SUCCESS)
    {
        float used_bytes = info.resident_size;
        float total_bytes = [NSProcessInfo processInfo].physicalMemory;
        //NSLog(@"Used: %f MB out of %f MB (%f%%)", used_bytes / 1024.0f / 1024.0f, total_bytes / 1024.0f / 1024.0f, used_bytes * 100.0f / total_bytes);
        return used_bytes / total_bytes;
    }
    return 1;
}

Swift (basé sur cette réponse ):

func __getMemoryUsedPer1() -> Float
{
    let MACH_TASK_BASIC_INFO_COUNT = (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t))
    let name = mach_task_self_
    let flavor = task_flavor_t(MACH_TASK_BASIC_INFO)
    var size = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)
    var infoPointer = UnsafeMutablePointer<mach_task_basic_info>.alloc(1)
    let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)
    let info = infoPointer.move()
    infoPointer.dealloc(1)
    if kerr == KERN_SUCCESS
    {
        var used_bytes: Float = Float(info.resident_size)
        var total_bytes: Float = Float(NSProcessInfo.processInfo().physicalMemory)
        println("Used: \(used_bytes / 1024.0 / 1024.0) MB out of \(total_bytes / 1024.0 / 1024.0) MB (\(used_bytes * 100.0 / total_bytes)%%)")
        return used_bytes / total_bytes
    }
    return 1
}
cprcrack
la source
1
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
Merci Speakus. Vous semblez avoir raison sur la base de ce lien . Avez-vous d'autres références où ces informations peuvent être trouvées?
cprcrack
Apple utilise aussi
Maxim Kholyavkin
45% n'est plus une limite sûre, il est trop proche de la valeur de crash de 50% pour l'iPhone X. Je suggère d'utiliser 40%, ou une valeur distincte pour chaque appareil.
Slyv
8

En forçant le repo SPLITS, j'en ai construit un pour tester la mémoire iOS qui peut être allouée à l'extension d'aujourd'hui

iOSMemoryBudgetTestForExtension

Voici le résultat que j'ai obtenu dans l'iPhone 5s

Avertissement de mémoire à 10 Mo

L'application a planté à 12 Mo

Par ce moyen, Apple permet simplement à toutes les extensions de fonctionner avec leur plein potentiel .

Dur
la source
7

Vous devriez regarder la session 147 des vidéos de la session 2010 de la WWDC . Il s'agit de "Optimisation avancée des performances sur iPhone OS, partie 2".
Il y a beaucoup de bons conseils sur l'optimisation de la mémoire.

Certains des conseils sont:

  • Utiliser imbriqué NSAutoReleasePool s pour vous assurer que votre utilisation de la mémoire n'augmente pas.
  • À utiliser CGImageSourcelors de la création de vignettes à partir d'images de grande taille.
  • Répondez aux avertissements de mémoire insuffisante.
Kobski
la source
Ma question n'est pas de savoir comment optimiser (merci pour le lien), mais de savoir combien pouvons-nous nous permettre d'utiliser. La raison en est que, par exemple, si nous optimisons pour gagner 20 Mo, alors les artistes voudront utiliser ces 20 Mo si cela se situe dans le "budget" raisonnable, c'est-à-dire sûr que cela ne causera pas de problèmes de performances ou de panne de mémoire.
frilla
D'ACCORD. Le plantage sera dû au fait que le système d'exploitation met fin à l'application en raison d'une mémoire limitée. Vous pouvez simplement ajouter un NSLogintérieur didReceiveMemoryWarning, puis faire des tests où vous allouez différentes quantités de mémoire, puis voyez quand les avertissements de mémoire commencent à
apparaître
4

À partir d'iOS13, il existe une méthode prise en charge par Apple pour l'interroger en utilisant

#include <os/proc.h>

size_t os_proc_available_memory(void)

Présenté ici: https://developer.apple.com/videos/play/wwdc2019/606/

Environ min 29-ish.

Edit: Ajout d'un lien vers la documentation https://developer.apple.com/documentation/os/3191911-os_proc_available_memory?language=objc

Exaberri Tokugawa
la source
Finalement! J'ai testé os_proc_available_memory () sur quelques appareils, et les résultats sont très similaires aux valeurs du grand tableau ci-dessus!
Slyv le
3
- (float)__getMemoryUsedPer1
{
    struct mach_task_basic_info info;
    mach_msg_type_number_t size = MACH_TASK_BASIC_INFO;
    kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
    if (kerr == KERN_SUCCESS)
    {
        float used_bytes = info.resident_size;
        float total_bytes = [NSProcessInfo processInfo].physicalMemory;
        //NSLog(@"Used: %f MB out of %f MB (%f%%)", used_bytes / 1024.0f / 1024.0f, total_bytes / 1024.0f / 1024.0f, used_bytes * 100.0f / total_bytes);
        return used_bytes / total_bytes;
    }
    return 1;
}

Si l'on utilise TASK_BASIC_INFO_COUNT au lieu de MACH_TASK_BASIC_INFO, vous obtiendrez

kerr == KERN_INVALID_ARGUMENT (4)

Dmitry Preobrazhenskiy
la source
Vous devriez au moins mentionner que votre réponse est presque un copier-coller exact de @ cprcrack ci - dessus. La seule différence est TASK_BASIC_INFO_COUNT.
mrvincenzo le
2

J'ai créé une autre liste en triant la liste de Jaspers par RAM de périphérique (j'ai fait mes propres tests avec l'outil de Split et j'ai corrigé quelques résultats - vérifiez mes commentaires dans le fil de discussion Jaspers).

RAM de l'appareil: plage de pourcentage pour planter

  • 256 Mo: 49% - 51%
  • 512 Mo: 53% - 63%
  • 1024 Mo: 57% - 68%
  • 2048 Mo: 68% - 69%
  • 3072 Mo: 63% à 66%
  • 4096 Mo: 77%
  • 6144 Mo: 81%

Cas spéciaux:

  • iPhone X (3072 Mo): 50%
  • iPhone XS / XS Max (4096 Mo): 55%
  • iPhone XR (3072 Mo): 63%
  • iPhone 11/11 Pro Max (4096 Mo): 54% - 55%

La RAM de l'appareil peut être lue facilement:

[NSProcessInfo processInfo].physicalMemory

D'après mon expérience, il est sûr d'utiliser 45% pour les appareils de 1 Go, 50% pour les appareils 2/3 Go et 55% pour les appareils 4 Go. Le pourcentage pour macOS peut être un peu plus grand.

Slyv
la source
mise à jour: il semble que l'iPhone X soit une exception - il se bloque lorsque 50% de la RAM est utilisée (testé avec l'application iOSMemoryBudgetTest). J'ai mis à jour la liste.
Slyv
0

En travaillant avec les nombreuses réponses ci-dessus, j'ai implémenté la nouvelle méthode Apples os_proc_available_memory()pour iOS 13+ couplée à NSByteCountFormatterqui offre un certain nombre d'options de formatage utiles pour une meilleure sortie de la mémoire:

#include <os/proc.h>

....

- (NSString *)memoryStringForBytes:(unsigned long long)memoryBytes {
    NSByteCountFormatter *byteFormatter = [[NSByteCountFormatter alloc] init];
    byteFormatter.allowedUnits = NSByteCountFormatterUseGB;
    byteFormatter.countStyle = NSByteCountFormatterCountStyleMemory;
    NSString *memoryString = [byteFormatter stringFromByteCount:memoryBytes];
    return memoryString;
}

- (void)memoryLoggingOutput {
    if (@available(iOS 13.0, *)) {
        NSLog(@"Physical memory available: %@", [self memoryStringForBytes:[NSProcessInfo processInfo].physicalMemory]);
        NSLog(@"Memory A (brackets): %@", [self memoryStringForBytes:(long)os_proc_available_memory()]);
        NSLog(@"Memory B (no brackets): %@", [self memoryStringForBytes:(long)os_proc_available_memory]);
    }
}

Remarque importante: n'oubliez pas le ()à la fin. J'ai inclus les deux NSLogoptions dans lememoryLoggingOutput méthode car elle ne vous avertit pas qu'elles sont manquantes et le fait de ne pas inclure les crochets renvoie un résultat inattendu mais constant.

La chaîne retournée par la méthode renvoie des memoryStringForBytesvaleurs comme ceci:

NSLog(@"%@", [self memoryStringForBytes:(long)os_proc_available_memory()]); // 1.93 GB
// 2 seconds later
NSLog(@"%@", [self memoryStringForBytes:(long)os_proc_available_memory()]); // 1.84 GB
Développeur d'applications
la source