web-dev-qa-db-fra.com

Android Comprendre les tailles de tas

Je suis relativement nouveau au développement Android et je n'arrive pas à saisir l'exception Java Mémoire insuffisante. Je sais que cela signifie que mon application a disparu sur le budget VM mais après avoir fait cette recherche sur Google plusieurs fois, je ne semble toujours pas comprendre ce concept. J'ai peur que mon application utilise trop de mémoire car j'ai six sélecteurs de boutons par écran avec deux bitmaps pour chaque sélecteur d'environ 20 Ko chacun selon l'onglet Propriétés. Sur mon G2x enraciné, j'ai défini le budget VM à 12 Mo, redémarré mon téléphone et exécuté mon application sans aucun problème) . Je dissocie les drawables sur chaque onDestroy () et je fais allusion au GC pour qu'il s'exécute ici également. Après avoir utilisé l'application pendant un certain temps dans l'émulateur, je clique sur "Cause GC" sur mon écran DDMS et les résultats sont ID = 1, taille du tas 6,133 Mo, 2,895 Mo alloués, 3,238 Mo libres,% utilisé 47,20, # objets 52623.

C'est là que je ne comprends pas ce qui se passe, mon émulateur est réglé sur 24 Mo de VM. Où est ce numéro? Le problème réel que j'ai est que si je mets l'émulateur à 16 Mo de VM mon application se bloque sur la deuxième activité avec l'exception de mémoire insuffisante. Comment se fait-il qu'il ne se bloque pas sur mon téléphone avec le VM réglé à 12 Mo ou sur mon ancien téléphone HTC Magic avec 12 Mo de stock VM? Vous pensez aussi que mon application prend trop de temps) Je ne sais pas si ces numéros DDMS sont bons ou non. Merci pour votre temps.

Quant à mon code, j'ai toutes les images spécifiées dans les mises en page XML, je ne fais rien avec elles par programmation, sauf pour y ajouter des écouteurs. J'ai trouvé ce morceau de code ici et je l'ai ajouté à chaque activité que j'ai ...

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

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

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

Sinon, tout ce que je fais, c'est ajouter onClickListeners aux boutons qui ont les arrière-plans PNG. Je voudrais apprendre à spécifier les arrière-plans des boutons par programmation, mais je dois avoir les fonctions de sélection comme sur le focus, sur la presse, non focalisé mais appuyé, etc. pour faire changer l'arrière-plan des boutons en fonction de l'interaction de l'utilisateur. J'ai passé en revue les documents à ce sujet, mais cela semble écrasant, c'est pourquoi j'ai pensé commencer par les bases de la gestion des tas et progresser jusqu'à la spécification de sélecteurs dans le code. Cela peut ne pas avoir de sens, mais y a-t-il une quantité "saine" d'allocation de mémoire qu'une application pourrait allouer sans se rapprocher de l'exception de mémoire insuffisante? Par exemple, si une application alloue 6 Mo, cela devrait être bien mais 8 Mo le poussent, y a-t-il des limites comme celle-ci dans l'allocation de mémoire? Merci encore Alex Lockwood pour votre réponse, je vais le lire et le relire encore jusqu'à ce que ce truc ait du sens pour moi

30
John P.

Lorsque vous définissez le budget VM sur votre émulateur/périphérique, ce que vous faites est de dire au tas la taille maximale autorisée. Au moment de l'exécution, le tas augmente dynamiquement en taille en tant que Dalvik VM demande de la mémoire système au système d'exploitation. Le Dalvik VM commence généralement par allouer un segment relativement petit. Ensuite, après chaque exécution du GC, il vérifie pour voir combien Si le rapport entre le tas libre et le tas total est trop petit, le Dalvik VM ajoutera alors plus de mémoire au tas (jusqu'à la taille de tas maximale configurée).

Cela étant dit, la raison pour laquelle vous ne voyez pas "24 Mo" sur votre écran DDMS est que le tas n'a pas atteint sa taille maximale. Cela permet à Android de faire bon usage de la quantité déjà faible de mémoire disponible sur les appareils portables.

Quant à savoir pourquoi votre application plante sur l'émulateur et non sur votre téléphone, cela semble étrange (êtes-vous sûr que les chiffres sont corrects?). Cependant, vous devez garder à l'esprit que la mémoire est gérée de manière dynamique et que l'utilisation totale de la mémoire est déterminée en fonction d'un certain nombre de facteurs externes (la vitesse/fréquence à laquelle le nettoyage de la mémoire est effectué, etc.).

Enfin, pour les raisons que j'ai mentionnées ci-dessus, il serait difficile de dire avec certitude dans quelle mesure votre application gère la mémoire en fonction de la seule ligne d'informations que vous avez fournie ci-dessus. Nous aurions vraiment besoin de voir une partie de votre code. OutOfMemoryErrors valent vraiment la peine de s'inquiéter, cependant, je vais donc certainement examiner l'utilisation de la mémoire de votre application. Une chose que vous pourriez envisager est d'échantillonner vos images bitmap lors de l'exécution avec des appels à inSampleSize à l'aide de la classe BitmapFactory. Cela peut aider à réduire la quantité de mémoire requise pour charger vos images bitmap pouvant être dessinées. Soit cela, soit vous pouvez réduire la résolution de vos tirables (bien que 20 ko chacun me semble bien).

51
Alex Lockwood

Même si votre application n'atteint pas la limite de segment de mémoire de "24 Mo" (varie selon les appareils), vous pouvez toujours avoir des plantages car Android prend un certain temps pour augmenter l'espace de segment pour votre application.

Dans ma situation, je créais et je vidais plusieurs images en peu de temps.

Très souvent, je recevais OutOfMemoryError.

Il semble Android n'a pas été assez rapide pour agrandir l'espace de stockage de mon application.

Je pense avoir résolu ce problème en utilisant le paramètre largeHeap dans le fichier manifeste. Lorsque ce paramètre est activé, Android laisse plus de mémoire libre à chaque fois qu'il augmente le tas, minimisant ainsi les chances d'atteindre la limite actuelle.

Je n'utilise pas la limite de 24 Mo mais cette conf largeHeap était assez pratique.

Il vous suffit de définir largeHeap="true" sur la balise d'application de votre AndroidManifest.xml

<application
    Android:icon="@drawable/ic_launcher"
    Android:label="@string/app_name"
    Android:largeHeap="true"
    Android:theme="@style/AppTheme" >

Cependant, assurez-vous d'être prudent lorsque vous traitez des images, comme l'a conseillé @Alex Lockwood.

18
tbraun