2009-12-22 3 views
29

J'ai quelques questions concernant les objets Bitmap et la mémoire et leur taxonomie générale.Bitmaps dans Android

  1. Qu'est-ce qu'un bitmap en mémoire ou natif?
  2. En quoi la mémoire Bitmap est-elle différente de la mémoire Heap?

Répondre

33

La mémoire qui sauvegarde un objet Bitmap est allouée à l'aide du code natif (malloc()), plutôt que le mot-clé Java new. Cela signifie que la mémoire est gérée directement par le système d'exploitation, plutôt que par Dalvik. La seule vraie différence entre le tas natif et le tas de Dalvik est que le tas de Dalvik est récupéré, et le tas natif ne l'est pas.

À ces fins cependant, voici peu de différence. Lorsque votre objet Bitmap est récupéré, son destructeur va recycler la mémoire associée dans le tas natif.

Source:

+0

Je suis actuellement confronté au même problème sur le tonnerre. Je suis en train de recycler les bitmaps, mais ils ne nettoient jamais l'espace sur le tas natif causant des erreurs oom. –

+5

Juste pour plus de clarté, la réponse ci-dessus est vrai pour android version 2.x et ci-dessous. À partir d'Android 3, les instances bitmap sont contre le tas. Cela peut facilement être vérifié: la création d'un bitmap dans Android 2.x laissera la taille du tas Java à peu près intouchée, la création dans Adnroid 3.x ajoutera beaucoup d'octets au tas Java. –

+0

Ah! alors vous dites que le comptage est maintenant beaucoup plus intuitif? C'est génial - sauf pour tous ceux qui ont écrit du code hacky pour additionner le tas Android avec le tas natif ... –

32

Il y a une subtilité importante ici: si les pixels Bitmap sont attribués dans le tas natif, quelques trucs spéciaux Dalvik la cause il doit être comptabilisé par rapport au tas Java. Ceci est fait pour deux raisons:

(1) Pour contrôler la quantité de mémoire allouée par une application. Sans la comptabilité, une application pourrait allouer une énorme quantité de mémoire (puisque l'objet Bitmap lui-même est très petit mais peut conserver une quantité arbitraire de mémoire native), dépassant la limite de 16 Mo ou 24 Mo.

(2) Pour aider à déterminer quand GC. Sans la comptabilité, vous pouvez allouer et libérer des références sur 100 objets bitmap, par exemple; le GC ne fonctionnerait pas, parce que ces objets sont minuscules, mais ils pourraient en fait représenter un grand nombre de mégaoctets d'allocations réelles qui ne sont pas gérées en temps opportun. En comptabilisant ces allocations sur le tas Java, le garbage collector s'exécutera car il pense que la mémoire est utilisée. Notez que, à bien des égards, il s'agit d'un détail d'implémentation; il est très probable qu'il pourrait changer dans le futur, bien que ce comportement de base resterait dans une certaine forme puisque ce sont deux caractéristiques importantes pour la gestion des allocations de bitmap.

+1

Même sur les téléphones qui comptent les bitmaps sur le tas java, le malloc natif peut allouer WAY plus de 16 MiB. –

+10

Le premier commentaire est faux, tous les téléphones comptent les allocations bitmap contre la limite de tas Dalvik. Le principal changement à prendre en compte est que depuis Android 3.0, les allocations sont réellement effectuées dans le tas de Dalvik, donc vous n'avez plus à faire face à des situations où le GC ne fonctionnerait pas aussi agressivement qu'il le devrait. – hackbod

+2

vous avez raison sur ce point. Pourtant, le GC est retardé lorsqu'il s'agit de traiter des bitmaps recyclés. J'ai trouvé un comportement imprévisible à moins que je n'exécute manuellement le GC après le recyclage d'un bitmap (y compris la tendance pour le "load" suivant à retourner une copie du bitmap recyclé au lieu de le charger du disque) –

12

De déploiements dans la nature, j'ai trouvé les appareils suivants:

  • dispositifs qui limitent à 16 Mo de tas java (bitmaps sont presque illimitées).
  • dispositifs qui limitent à 16 Mo de (en tas java + stockage bitmap natif)
  • dispositifs qui limitent à 24 Mo de tas de Java (bitmaps sont presque illimitée).
  • dispositifs qui limitent à 24 Mo de (en tas java + stockage bitmap natif)

24 MiB a tendance à être des dispositifs à haute résolution, et peuvent être détectés avec Runtime.getRuntime(). MaxMemory(). Il y a aussi des périphériques 32MiB maintenant, et certains téléphones enracinés ont 64MiB par défaut. Auparavant, je me suis confus plusieurs fois en essayant de comprendre ce qui se passait. Je pense que tous les appareils comptent les bitmaps dans la limite du tas. Mais il est extrêmement difficile de faire des généralisations générales sur la flotte Android.

Ceci est un problème très vicieux sur Android, et très confus. Cette limite et ses comportements sont mal documentés, compliqués et extrêmement non intuitifs. Ils varient également selon les périphériques et les versions du système d'exploitation et comportent plusieurs bogues connus. Une partie du problème est que les limites ne sont pas précises - en raison de la fragmentation du tas, vous frapperez le MOO bien avant la limite réelle et devrez laisser prudemment un méga ou deux de tampon. Pire encore, j'ai plusieurs périphériques où il y a un segfault natif (100% un bug dans Android lui-même) qui se produit avant que vous obteniez l'exception java MOO, ce qui rend l'important de ne jamais atteindre la limite car vous ne pouvez même pas attraper le crash indigène. Pour plus de détails sur mes enquêtes, consultez this post. Dans le même article, j'explique comment mesurer l'utilisation par rapport à la limite et éviter les plantages.

La taille du tas java est Runtime.getRuntime(). TotalMemory().

Il n'existe pas de moyen simple de mesurer la taille du stockage bitmap natif. Le tas natif global peut être mesuré avec Debug.getNativeHeapAllocatedSize(), mais seuls les bitmaps comptent vers la limite (je pense).

+0

Puisque n'importe qui peut sortir sa propre version d'Android, il semble tout à fait crédible qu'il puisse y avoir des versions qui implémentent des politiques différentes pour limiter la mémoire de tas, y compris celles que vous décrivez ci-dessus. CyanogenMod, par exemple, permet aux utilisateurs de définir eux-mêmes la limite de tas, donc je ne vois pas pourquoi il ne pourrait pas y avoir une version de système d'exploitation qui implémente les politiques concernant les limites que vous décrivez. Je serais intéressé de savoir s'il existe des directives officielles à cet égard. Sinon, nous sommes soumis aux caprices de ces variantes d'OS. – Carl

+0

@Carl.D'après ce que je peux dire, c'est du chaos pur. Bien qu'en général, les mods et les téléphones enracinés tendent à augmenter les limites de tas, typiquement à 64M. La décision de limiter si sévèrement la taille du tas Java a été retardée par le cerveau et entraîne des expériences utilisateur horribles. –

+0

Cela nous force en tant que développeurs à regarder de près notre utilisation de la mémoire. Je développe actuellement une application qui a un très grand ensemble de données qui doit résider entièrement sur le tas (à cause d'un processus de recherche intensif), et mon implémentation basée sur le tableau String a pris 15 Mo de tas pour ces données seulement (chaque String ref dans le tableau prend 32 octets de données). Après avoir presque atteint la limite de 24 Mo que beaucoup de périphériques actuels imposent encore, j'ai conçu une structure de données personnalisée qui ne prend que 3,5 Mo pour contenir exactement la même information. Maintenant, mon application fonctionnera même sur un appareil avec une limite de 16 Mo. – Carl

-1

Nous pouvons augmenter la taille du tas en utilisant android:largeheap="true" dans votre fichier manifeste. Cela va résoudre votre problème. Savez-vous si ce bogue a déjà été corrigé?

+0

Pensez à la batterie de votre utilisateur ... Demander un gros tas pour couvrir certaines erreurs sur les allocations de mémoire, ne peut clairement pas être une solution. – JuSchz

Questions connexes