Besoins importants en mémoire sous Android - que faire?

Bonjour chers lecteurs.

Aujourd'hui, nous portons à votre attention un peu de matériel sur l'utilisation compétente de la mémoire dans Android .



Bonne lecture!

Cet article se concentre sur les techniques de base pour gérer l'utilisation de la mémoire dans les applications - par exemple, les navigateurs, les éditeurs de photos et les visualiseurs PDF - dans lesquelles de grandes demandes de mémoire sont effectuées.

Tout d'abord, un peu de théorie


La plupart des applications Android fonctionnent au-dessus du runtime ( ART ), qui a remplacé la machine virtuelle Dalvik désormais obsolète. ART et Dalvik sont similaires à la machine virtuelle Java (JVM), avec laquelle ils partagent des principes de conception similaires. Ils utilisent deux espaces distincts pour stocker les données d'application: la pile et le tas.

Stack-memory

Stack-memory en Java est utilisé pour stocker des variables locales (types primitifs et références d'objet). Chaque thread Java a sa propre pile distincte. La mémoire de pile est relativement petite par rapport à la mémoire de tas. La taille de la pile Java de Dalvik est généralement de 32 Ko pour le code Java et de 1 Mo pour le code natif (C ++ / JNI). Une pile unifiée pour Java et C ++ est apparue dans ART, dont la taille est d'environ 1 Mo.

Lorsque l'application sélectionne la mémoire de pile entière à la limite, une erreur est levée StackOverflowError. La raison la plus probable pour laquelle une limite de pile peut être atteinte est soit une récursion infinie, soit un appel de méthode trop profond. Les références à la mémoire de la pile sont toujours faites dans l'ordre LIFO (dernier arrivé, premier servi). Chaque fois qu'une méthode est appelée, une nouvelle trame est poussée sur la pile avec les variables locales de cette méthode. Une fois la méthode terminée, son cadre est extrait de la pile et toute valeur résultante possible est renvoyée à la pile. Ainsi, le premier problème (récursion infinie) est un bogue facile à corriger, mais le second nécessite une refactorisation, qui consiste à déployer des appels de méthode récursifs et à les convertir en boucle.

Mémoire de tas

La mémoire de tas en Java est utilisée par une machine virtuelle pour allouer des objets. Chaque fois qu'un objet est créé, il se produit sur le tas. Les machines virtuelles, telles que la JVM ou ART, collectent régulièrement les déchets, suppriment tous les objets qui ne sont plus référencés et libèrent ainsi de la mémoire pour allouer de nouveaux objets.
Pour garantir la convivialité, Android limite étroitement la taille des segments de mémoire pour chaque application en cours d'exécution. La limite de taille de segment de mémoire varie d'un appareil à l'autre et dépend de la quantité de RAM sur cet appareil. Si votre application atteint la taille de segment de mémoire maximale et tente d'allouer plus de mémoire, une erreur est générée OutOfMemoryErroret l'application se termine. Regardons quelques exemples pour éviter cette situation.

Analyse de la mémoire de tas


L'outil le plus important pour comprendre les problèmes de mémoire dans vos applications et comprendre comment la mémoire est utilisée est le profileur de mémoire disponible dans Android Studio.

Cet outil visualise la quantité de mémoire que votre application consomme au fil du temps. Vous pouvez prendre des instantanés du tas Java dans une application en cours d'exécution, enregistrer les opérations d'allocation de mémoire et surveiller le tas ou cet historique d'allocation de mémoire dans une interface utilisateur puissante.

Une session typique de profileur de mémoire devrait ressembler à ceci:

  • Nous examinons les allocations de mémoire et les passages de garbage collector les plus fréquents pour identifier les problèmes de performances possibles.
  • , , , , , . , . , , PdfActivity PSPDFKit .
  • , . , . – , , .


Les éboueurs modernes sont des œuvres d'art technologique complexes, le résultat de nombreuses années de recherche et développement, auxquelles des centaines de personnes ont participé, des universitaires aux développeurs professionnels. Cependant, vous devez toujours être en alerte pour éviter les fuites de mémoire.

Une solution exemplaire pour détecter les fuites de mémoire est la bibliothèque LeakCanary . Il émet automatiquement des notifications dans votre assembly de test (build de développement), vous donnant le taux de trace de fuite dans l'interface utilisateur de ce programme. Vous pouvez (et devez) l' intégrer dès aujourd'hui, d'autant plus que ce n'est pas difficile!

Il est particulièrement facile de provoquer des fuites de mémoire lorsque vous travaillez avec des cycles de vie complexes d'activités ou des fragments d'Android. Cela se produit souvent aux points où les développeurs détiennent des références fortes aux contextes d' interface utilisateur ou à d'autres objets spécifiques à l'interface utilisateur dans la tâche d'arrière-plan ou dans des variables statiques. Une façon de provoquer de tels retards est de tordre activement l'appareil lors du test de votre application.

Libérez de la mémoire en réponse aux événements


Android peut exiger que l'application alloue de la mémoire, ou simplement la forcer à se terminer lorsque la mémoire doit être libérée pour effectuer des tâches plus critiques. Avant cela, le système vous permettra de donner toute la mémoire dont vous n'avez pas besoin. Dans votre activité, vous devrez implémenter une interface ComponentCallbacks2. Dans ce cas, chaque fois que votre système manque de mémoire, un appel sera effectué vers votre méthode onTrimMemory()et vous pourrez libérer de la mémoire ou désactiver des fonctionnalités qui ne fonctionneront pas dans de telles conditions de manque de mémoire.

Ainsi, ces rappels sont traités dans l'application PSPDFKit. L'application PSPDFKit a été conçue avec le calcul de l'utilisation active de la mémoire pour la mise en cache, afin que l'application s'exécute aussi bien que possible. Initialement, on ne sait pas combien de mémoire est disponible sur l'appareil, donc PSPDFKit s'adapte à la situation et limite l'utilisation de la mémoire lorsqu'il reçoit des notifications indiquant qu'il n'y a pas assez de mémoire. Par conséquent, les applications intégrées à PSPDFKit fonctionnent même sur des appareils à faible technologie, mais avec des performances réduites en raison du fait que la mise en cache est désactivée.

Gros tas


L'une des solutions frontales pour faire face aux exigences de mémoire élevées est de demander un grand nombre de Dalvik pour votre application. Pour ce faire, vous pouvez ajouter android:largeHeap="true"à la balise <application> dans le fichier AndroidManifest.xml.

Si la propriété largeHeapest définie sur value true, Android créera tous les processus pour votre application avec un grand tas. Ce paramètre est destiné uniquement aux applications qui, de par leur nature, ne peuvent pas fonctionner sans lui, c'est-à-dire qu'elles utilisent des ressources volumineuses qui doivent simultanément tenir en mémoire.

Il est fortement déconseillé d'utiliser un grand tas si vous souhaitez uniquement augmenter le plafond pour une éventuelle utilisation de la mémoire. L'utilisation de la mémoire doit toujours être optimisée, car même une grande pile de votre application peut ne pas suffire lorsque vous travaillez sur un appareil faible avec une petite mémoire.

Vérifiez la quantité de mémoire que votre application peut utiliser


Il n'est jamais inutile de vérifier la taille du tas de votre application et d'adapter dynamiquement votre code et les capacités disponibles à ces limites de mémoire. Vous pouvez vérifier la taille maximale du segment directement lors de l'exécution à l'aide de méthodes getMemoryClass()ou getLargeMemoryClass()(lorsqu'un grand segment est activé).

Android prend même en charge les appareils avec seulement 512 Mo de RAM. Assurez-vous de ne pas ignorer les appareils low-tech! Utilisation de la méthodeisLowRamDevice()Vous pouvez vérifier si votre application s'exécute sur un tel appareil où la mémoire disponible est insuffisante. Le comportement exact de cette méthode dépend du périphérique, mais il retourne généralement vrai sur les périphériques avec moins de 1 Go de RAM. Vous devez vous assurer que votre application fonctionne correctement sur ces appareils et désactiver toutes les fonctionnalités qui utilisent une grande quantité de mémoire sur ces appareils.

En savoir plus sur le fonctionnement d'Android sur les appareils avec une petite quantité de mémoire, vous pouvez lire ici ; Des conseils d'optimisation supplémentaires sont également fournis ici.

Utiliser des structures de données optimisées


Dans de nombreux cas, les applications utilisent trop de mémoire pour la simple raison qu'elles n'utilisent pas les structures de données les plus appropriées.

Les collections Java ne peuvent pas stocker de types primitifs efficaces et nécessitent de compresser leurs clés et leurs valeurs. Par exemple, HashMaples clés entières doivent être remplacées par des clés optimisées SparseArray. En fin de compte, vous pouvez toujours utiliser des tableaux bruts au lieu de collections, et c'est une excellente idée si votre collection n'est pas redimensionnable.

D'autres structures de données qui sont inefficaces en termes d'utilisation de la mémoire incluent diverses sérialisations. Oui, en effet, les formats XML ou JSON sont pratiques à utiliser, vous pouvez réduire l'utilisation de la mémoire si vous travaillez avec un format binaire plus efficace, par exemple, des tampons de protocole.

Tous ces exemples, avec des structures de données optimisées pour économiser de la mémoire, ne sont que des conseils. Comme pour le refactoring, vous devez d'abord trouver la source des problèmes, puis passer à de telles optimisations de performances.

Empêcher le brassage de la mémoire


Les machines virtuelles Java / Android allouent les objets très rapidement. La collecte des ordures est également très rapide. Cependant, lors de l'allocation d'un grand nombre d'objets dans un court laps de temps, vous pouvez rencontrer un problème appelé «désabonnement de la mémoire». Dans ce cas, la machine virtuelle n'aura pas le temps d'allouer des objets à ce rythme, et le garbage collector les éliminera, et l'application commencera à ralentir, et dans les cas extrêmes, elle utilisera même toute la mémoire.

Le principal problème sur le territoire Android dans ce cas est que nous ne contrôlons pas le moment où la collecte des ordures aura lieu. Potentiellement, cela peut entraîner des problèmes: par exemple, le garbage collector fonctionne exactement au moment où l'animation se déroule à l'écran et nous dépassons le seuil de 16 ms, ce qui garantit un affichage fluide des images. Par conséquent, il est important d'éviter la surallocation de mémoire dans le code.

Un exemple de situation qui entraîne un brassage de la mémoire est l'allocation d'objets volumineux, par exemple, Paint à l'intérieur de la méthode de onDraw()présentation. Dans ce cas, de nombreux objets sont rapidement créés et la récupération de place peut commencer, ce qui peut nuire aux performances de cette vue. Comme indiqué ci-dessus, vous devez toujours surveiller l'utilisation de la mémoire pour éviter de telles situations.

Conclusion


La mémoire vive (RAM) sur les appareils mobiles peut être une ressource très limitée. Il est particulièrement important de garantir l'utilisation efficace de la mémoire dans l'application si votre application fonctionne avec des objets relativement volumineux, par exemple des graphiques raster (visionneuses PDF, navigateurs Web, éditeurs de photos) ou de gros fichiers multimédias (éditeurs audio ou vidéo). En suivant ces conseils, vous apprendrez à créer des applications de haute qualité qui fonctionneront à un niveau acceptable, même sur les appareils les plus puissants.

All Articles