63 votes

Comment puis-je éviter les retards de récupération des ordures dans les jeux Java? (Les meilleures pratiques)

Je suis d'optimisation des performances des jeux interactifs en Java pour la plateforme Android. De temps en temps il y a un problème dans le dessin et l'interaction pour la collecte des ordures. Habituellement, c'est moins d'un dixième de seconde, mais parfois il peut être aussi grand que 200ms sur très lent appareils.

Je suis l'aide de la ddms profiler (partie du SDK Android) à la recherche où mes allocations de mémoire à venir, et de l'accise de mon intérieur du dessin et de la logique des boucles.

Le pire délinquant avait été de courte boucle de fait,

for(GameObject gob : interactiveObjects)
    gob.onDraw(canvas);

où chaque fois que la boucle a été exécuté il y a un iterator alloués. Je suis à l'aide de tableaux (ArrayList) pour mes objets maintenant. Si jamais je veux des arbres ou des hachages dans une boucle interne, je sais que j'ai besoin d'être plus prudent ou même les réimplémenter au lieu d'utiliser le Java Collections cadre, car je ne peux pas se permettre de payer le supplément de la collecte des ordures. Qui peut venir quand je suis à la recherche des files d'attente de priorité.

J'ai aussi des problèmes lorsque je veux afficher les scores et les progrès à l'aide de Canvas.drawText. Ce qui est mauvais,

canvas.drawText("Your score is: " + Score.points, x, y, paint);

parce qu' Strings, char tableaux et StringBuffers seront alloués pour le faire fonctionner. Si vous avez un peu de texte pour l'affichage des éléments et exécuter le cadre 60 fois par seconde, qui commence à ajouter et permettra d'augmenter votre collecte des ordures hoquet. Je pense que le meilleur choix est de garder char[] tableaux et décoder vos int ou double manuellement et concaténer des chaînes sur le début et la fin. Je voudrais savoir si il y a quelque chose de plus propre.

Je sais il doit y avoir d'autres là-bas en occuper. Comment gérez-vous et quels sont les écueils et les meilleures pratiques que vous avez découvert à exécuter de manière interactive sur Java ou Android? Ces gc questions sont assez pour me faire manquer manuel de gestion de la mémoire, mais pas beaucoup.

56voto

SyntaxT3rr0r Points 10771

J'ai travaillé sur mobile Java jeux... La meilleure façon d'éviter GC avec des objets (qui doit déclencher la GC à un moment ou à un autre et doit tuer votre jeu de perfs) est tout simplement pour éviter de les créer dans votre boucle de jeu en premier lieu.

Il n'y a pas de manière "propre" et je vais vous donner un exemple...

En général, vous avez, disons, 4 balles sur l'écran (50,25), (70,32), (16,18), (98,73). Et bien, voici l'abstraction (simplifié pour les besoins de cet exemple):

n = 4;
int[] { 50, 25, 70, 32, 16, 18, 98, 73 }

Vous "pop" de la 2ème balle qui disparaît, votre int[] devient:

n = 3
int[] { 50, 25, 98, 73, 16, 18, 98, 73 }

(notez que nous ne se soucient même pas de "nettoyage" de la 4ème balle (98,73), nous avons tout simplement garder une trace du nombre de balles nous ont de gauche).

Le manuel de suivi d'objets, malheureusement. Cette façon de faire sur la plupart des bien-exécution Java jeux qui sont sortis sur des appareils mobiles.

Maintenant, pour les cordes, voici ce que je ferais:

  • lors de l'initialisation de la partie, predraw à l'aide de drawText(...) *une seule fois* les nombres de 0 à 9 que vous enregistrez dans un BufferedImage[10] tableau.
  • lors de l'initialisation de la partie, predraw une fois "Votre score est de: "
  • si le "Votre score est de: " a vraiment besoin d'être redessiné (parce que, disons, c'est transparent), puis redessiner à partir de votre pré-enregistrées BufferedImage
  • boucle pour calculer les chiffres de la partition et d'ajouter, après le "Votre score est de: ", tous les chiffres à la main un par un (en copiant les uns le temps des chiffres (0 à 9) à partir de votre BufferedImage[10] où vous pré-enregistrés.

Cela vous donne le meilleur des deux monde: vous obtenez la réutilisation de la drawtext(...) la police et vous avez créé exactement zéro des objets lors de votre boucle principale (parce que vous aussi esquivé l'appel à drawtext(...) qui lui-même peut très bien être crappily génération, eh bien, inutile de la merde).

Un autre "avantage" de cette "zéro de la création d'objet de tirage de score" est qu'une image mise en cache et de la réutiliser pour les polices de caractères n'est pas vraiment "manuel de l'objet de l'allocation/libération de la mémoire", c'est vraiment juste attention à la mise en cache.

Ce n'est pas "propre", ce n'est pas une "bonne pratique", mais c'est la façon dont c'est fait dans le haut de gamme de jeux mobiles (comme, par exemple, Uniwar).

Et c'est rapide. Sacrément rapide. Plus vite que quelque chose impliquant la création de l'objet.

P. S: en Fait, si vous regardez bien quelques jeux mobiles, vous remarquerez que souvent les polices ne sont pas réellement de système/Java polices mais pixel-perfect polices faite spécifiquement pour chaque jeu (ici, je vous ai juste donné un exemple de système de cache/Java police mais, évidemment, vous pouvez aussi cache/réutilisation d'un pixel-perfect/police bitmap).

15voto

Eonil Points 19404

Si c'est un 2 ans question...

La seule et la meilleure approche pour éviter GC décalage est d'éviter GC lui-même par l'affectation de tous les objets requis un peu statique (y compris au démarrage). Avant de créer tous les objets requis, et de ne jamais faire de à être supprimé. L'utilisation de l'objet le regroupement de la ré-utilisation d'un objet existant.

De toute façon vous pouvez avoir une éventuelle pause, même après que vous avez fait toutes les optimisations possibles sur votre code. Parce que rien d'autre que votre code d'application est encore en train de créer GC objets en interne , qui vont devenir les ordures par la suite. Par exemple, Java de base de la bibliothèque. Même en utilisant de simples List classe peut créer de poubelles. (donc, doit être évité) tout Appel de l'API Java peut créer de poubelles. Et ces attributions ne sont pas évitables pendant que vous êtes à l'aide de Java.

Aussi, parce que Java est conçu pour utiliser les GC, vous aurez du mal par le manque de fonctionnalités si vous avez réellement essayez d'éviter de GC. (même List de la classe doit être évitée), Car il permet à la GC, toutes les bibliothèques peuvent utiliser GC, de sorte que vous pratiquement/presque pas de bibliothèque. Je considère en évitant GC sur GC en fonction de la langue est une sorte de fou du procès.

En fin de compte, la seule façon est d'aller vers le bas pour abaisser le niveau où vous pouvez contrôler la mémoire vous-même complètement. Comme C la famille de langages (C, C++, etc.). Alors, allez à NDK.

Note

Maintenant, Google est la livraison incrémentielle (concurrent?) GC qui peut diminuer la pause beaucoup. De toute façon incrémentielle GC, c'est tout simplement la distribution de GC de charge dans le temps, de sorte que vous pouvez encore voir une éventuelle pause si la distribution n'est pas l'idéal. Aussi GC performance elle-même sera réduit en raison des effets secondaires de moins de dosage et de distribution frais généraux.

7voto

David Scherfgen Points 95

J'ai construit ma propre nettoyage de la version gratuite, String.format, au moins. Vous le trouverez ici: http://pastebin.com/s6ZKa3mJ (veuillez excuser l'allemand commentaires).

L'utiliser comme ceci:

GFStringBuilder.format("Your score is: % and your name is %").eat(score).eat(name).result

Tout est écrit en char[] tableau. J'ai eu à mettre en œuvre la conversion d'entier en chaîne manuellement (digit par digit) pour se débarrasser de tous les déchets.

En dehors de cela, j'utilise SparseArray si possible, car toutes les Java structures de données telles que HashMap, ArrayList etc. utiliser de boxe afin de traiter avec des types primitifs. Chaque fois que vous zone une int de Integerce Integer objet doit être nettoyé par le GC.

4voto

gustafc Points 13552

Si vous ne souhaitez pas pré-afficher le texte tel que proposé, drawText accepte tous les CharSequence ce qui signifie que nous pouvons en faire notre propre implémentation intelligente:

 final class PrefixedInt implements CharSequence {

    private final int prefixLen;
    private final StringBuilder buf;
    private int value; 

    public PrefixedInt(String prefix) {
        this.prefixLen = prefix.length();
        this.buf = new StringBuilder(prefix);
    }

    private boolean hasValue(){
        return buf.length() > prefixLen;
    }

    public void setValue(int value){
        if (hasValue() && this.value == value) 
            return; // no change
        this.value = value;
        buf.setLength(prefixLen);
        buf.append(value);
    }


    // TODO: Implement all CharSequence methods (including 
    // toString() for prudence) by delegating to buf 

}

// Usage:
private final PrefixedInt scoreText = new PrefixedInt("Your score is: ");
...
scoreText.setValue(Score.points);
canvas.drawText(scoreText, 0, scoreText.length(), x, y, paint);
 

Maintenant, dessiner le score ne cause aucune allocation (sauf peut-être une ou deux fois au début, quand il faudra peut-être agrandir le tableau interne de buf s, et quel que soit le drawText ).

3voto

Stephen C Points 255558

Dans une situation où il est essentiel pour éviter de GC pauses, un truc que vous pouvez utiliser est délibérément déclencher GC à un point où vous savez que la pause n'a pas d'importance. Par exemple, si le garbage intensif "showScores" fonction est utilisée à la fin d'un jeu, l'utilisateur de ne pas être trop distrait par un supplément de 200 ms délai entre l'affichage de l'écran de score et de commencer le prochain match ... qu'on pourrait qualifier d' System.gc() une fois que les scores de l'écran a été peint.

Mais si vous avez recours à cette astuce, vous devez être prudent de le faire seulement en des points où le GC pause ne sera pas ennuyeux. Et de ne pas le faire si vous êtes inquiet au sujet de drainage de la batterie du combiné.

Et de ne pas le faire en multi-utilisateurs ou non des applications interactives parce que vous aurez plus de chances de rendre l'application plus lent globale en faisant cela.

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