159 votes

java.lang.OutOfMemoryError : la taille du bitmap dépasse le budget de la VM - Android

J'ai développé une application qui utilise beaucoup d'images sur Android.

L'application s'exécute une fois, remplit les informations sur l'écran ( Layouts , Listviews , Textviews , ImageViews etc.) et l'utilisateur lit l'information.

Il n'y a pas d'animation, pas d'effets spéciaux ou quoi que ce soit qui puisse remplir la mémoire. Parfois, les éléments à dessiner peuvent changer. Certains sont des ressources Android et d'autres des fichiers enregistrés dans un dossier de la SDCARD.

Ensuite, l'utilisateur quitte (le onDestroy est exécutée et l'application reste dans la mémoire de la VM), puis à un moment donné, l'utilisateur entre à nouveau.

Chaque fois que l'utilisateur entre dans l'application, je peux voir la mémoire s'accroître de plus en plus jusqu'à ce que l'utilisateur obtienne l'accès à l'application. java.lang.OutOfMemoryError .

Quelle est donc la meilleure façon de gérer de nombreuses images ?

Devrais-je les placer dans des méthodes statiques afin qu'elles ne soient pas chargées en permanence ? Dois-je nettoyer la mise en page ou les images utilisées dans la mise en page d'une manière particulière ?

97voto

hp.android Points 2201

L'une des erreurs les plus courantes que je rencontre en développant des applications Android est l'erreur "java.lang.OutOfMemoryError : Bitmap Size Exceeds VM Budget". J'ai trouvé cette erreur fréquemment sur des activités utilisant beaucoup de bitmaps après un changement d'orientation : l'activité est détruite, recréée et les layouts sont "gonflés" à partir du XML consommant la mémoire VM disponible pour les bitmaps.

Les bitmaps de l'activité précédente ne sont pas correctement désalloués par le ramasseur d'ordures car ils ont des références croisées avec leur activité. Après de nombreuses expériences, j'ai trouvé une assez bonne solution à ce problème.

Tout d'abord, définissez l'attribut "id" sur la vue parent de votre mise en page XML :

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Ensuite, sur le onDestroy() de votre activité, appelez la méthode unbindDrawables() en passant une référence à la vue parent et ensuite faire un System.gc() .

    @Override
    protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
    }

    private void unbindDrawables(View view) {
        if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
        ((ViewGroup) view).removeAllViews();
        }
    }

Ce site unbindDrawables() La méthode explore l'arbre de vue de manière récursive et :

  1. Supprime les callbacks sur tous les éléments d'arrière-plan.
  2. Supprime les enfants de chaque groupe de vues

72voto

Trevor Johns Points 9310

Il semble que vous ayez une fuite de mémoire. Le problème n'est pas de gérer beaucoup d'images, c'est que vos images ne sont pas désallouées lorsque votre activité est détruite.

Il est difficile de dire pourquoi sans regarder votre code. Cependant, cet article propose quelques conseils qui pourraient vous aider :

http://Android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html

En particulier, l'utilisation de variables statiques risque d'aggraver les choses, et non de les améliorer. Il se peut que vous deviez ajouter du code qui supprime les callbacks lorsque votre application redessine les images, mais là encore, les informations sont insuffisantes pour en être sûr.

11voto

ddmytrenko Points 191

Pour éviter ce problème, vous pouvez utiliser la méthode native Bitmap.recycle() avant null -ing Bitmap (ou en définissant une autre valeur). Exemple :

public final void setMyBitmap(Bitmap bitmap) {
  if (this.myBitmap != null) {
    this.myBitmap.recycle();
  }
  this.myBitmap = bitmap;
}

Et ensuite vous pouvez changer myBitmap sans appel System.gc() comme :

setMyBitmap(null);    
setMyBitmap(anotherBitmap);

8voto

Donn Felker Points 3501

J'ai rencontré exactement le même problème. Le tas est assez petit, ce qui fait que ces images peuvent rapidement devenir incontrôlables en termes de mémoire. Une solution consiste à donner au ramasseur d'ordures une indication pour collecter la mémoire sur un bitmap en appelant sa méthode de recyclage .

De plus, il n'est pas garanti que la méthode onDestroy soit appelée. Vous pouvez déplacer cette logique/ce nettoyage dans l'activité onPause. Consultez le diagramme/tableau du cycle de vie des activités. sur cette page pour plus d'informations.

7voto

Aron Points 54

Cette explication pourrait vous aider : http://code.google.com/p/Android/issues/detail?id=8488#c80

"Conseils rapides :

1) N'appelez JAMAIS System.gc() vous-même. Cela a été propagé comme une correction ici, et cela ne fonctionne pas. Ne le faites pas. Si vous avez remarqué dans mon explication, avant d'obtenir un OutOfMemoryError, la JVM a déjà effectué un garbage collection, il n'y a donc aucune raison d'en refaire un (cela ralentit votre programme). En faire une à la fin de votre activité ne fait que masquer le problème. Il se peut que le bitmap soit placé plus rapidement dans la file d'attente des finaliseurs, mais il n'y a aucune raison pour que vous ne puissiez pas simplement appeler recycle sur chaque bitmap à la place.

2) Appelez toujours recycle() sur les bitmaps dont vous n'avez plus besoin. Au minimum, dans le onDestroy de votre activité, passez en revue et recyclez tous les bitmaps que vous utilisiez. De plus, si vous voulez que les instances de bitmap soient récupérées plus rapidement du tas dalvik, cela ne fait pas de mal d'effacer toutes les références au bitmap.

3) L'appel à recycle() et ensuite à System.gc() peut ne pas supprimer le bitmap du tas Dalvik. NE VOUS INQUIÉTEZ PAS à ce sujet. recycle() a fait son travail et a libéré la mémoire native, il faudra juste un peu de temps pour suivre les étapes que j'ai décrites plus tôt pour supprimer réellement le bitmap du tas Dalvik. Ce n'est PAS un gros problème car la grande partie de la mémoire native est déjà libre !

4) Toujours supposer qu'il y a un bogue dans le framework en dernier. Dalvik fait exactement ce qu'il est censé faire. Ce n'est peut-être pas ce que vous attendez ou ce que vous voulez, mais c'est comme ça que ça fonctionne. "

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X