11 conseils basiques améliorant les performances en Java (Consultants junior)

A partir d’un certain niveau, le but n’est plus seulement d’écrire du code qui marche, mais qui soit performant. Voici quelques étapes que vous pouvez suivre pour éliminer les goulots d’étranglement, les astuces de mise en cache et d’autres suggestions d’optimisation des performances.

 

La plupart des développeurs s’attendent à ce que l’optimisation des performances soit un sujet compliqué nécessitant beaucoup d’expérience et de connaissances. D’accord, ce n’est pas entièrement faux. Optimiser une application pour obtenir les meilleures performances possibles n’est pas une tâche facile. Mais cela ne signifie pas que vous ne pouvez rien faire si vous n’avez pas acquis cette connaissance. Il existe plusieurs recommandations faciles à suivre et les meilleures pratiques qui vous aident à créer une application performante.

La plupart de ces recommandations sont spécifiques à Java. Mais il y en a aussi qui sont indépendant du langage que vous utilisez. Parlons de quelques-uns de ces génériques avant d’en arriver aux conseils d’optimisation des performances spécifiques à Java.

1. Ne pas optimiser avant de savoir si c’est vraiment nécessaire

Cela pourrait être l’un des conseils les plus importants de réglage des performances. Vous devez suivre les meilleures pratiques courantes et essayer de mettre en œuvre vos cas d’utilisation de manière efficace. Mais cela ne signifie pas que vous devez remplacer des bibliothèques standard ou construire des optimisations complexes avant de prouver que c’est nécessaire.

Dans la plupart des cas, l’optimisation prématurée prend beaucoup de temps et rend le code difficile à lire et à maintenir. Et pour empirer les choses, ces optimisations n’offrent généralement aucun avantage car vous passez beaucoup de temps à optimiser les parties non critiques de votre application.

Alors, comment savoir si vous avez besoin d’optimiser quelque chose?

Tout d’abord, vous devez définir la vitesse de votre code d’application, par exemple en spécifiant un temps de réponse maximum pour tous les appels d’API ou le nombre d’enregistrements que vous souhaitez importer dans un laps de temps spécifié. Après avoir fait cela, vous pouvez identifiées quelles parties de votre application sont trop lentes et doivent être améliorées. Cela se fait par la prise de différentes mesures. Et quand vous avez fait cela, vous devriez jeter un oeil à la deuxième astuce.

2. Utilisez un profileur pour trouver le vrai goulot d’étranglement

Après avoir suivi la première recommandation et identifié les parties de votre application que vous devez améliorer, demandez-vous par où commencer.

Vous pouvez aborder cette question de deux façons:

  • Vous pouvez jeter un coup d’œil à votre code et commencer par la partie qui vous semble suspecte ou dans laquelle vous pensez que cela pourrait créer des problèmes.
  • Ou vous utilisez un profileur et obtenez des informations détaillées sur le comportement et les performances de chaque partie de votre code.

J’espère que je n’ai pas besoin d’expliquer pourquoi vous devriez toujours suivre la deuxième approche.

Il devrait être évident que la méthode basée sur le profileur vous donne une meilleure compréhension des implications de votre code en termes de performances et vous permet de vous concentrer sur les parties les plus critiques. Et si vous avez déjà utilisé un profileur, vous vous souviendrez de quelques situations dans lesquelles vous avez été surpris par les parties de votre code qui ont créé les problèmes de performances. Plus d’une fois, ma première supposition m’aurait conduit dans la mauvaise direction.

3. Créez une suite de tests de performances pour l’ensemble de l’application

Ceci est une autre astuce générale qui vous aide à éviter beaucoup de problèmes inattendus qui se produisent souvent après que vous avez déployé en production, l’amélioration faite sur la performance. Vous devez toujours définir une suite de tests de performances qui teste l’ensemble de l’application et ensuite l’exécuter avant et après avoir amélioré les performances.

Ces tests supplémentaires vous aideront à identifier les effets secondaires fonctionnels, de performance, liés aux modifications que vous avez faites et à vous assurer que vous ne livrez pas une mise à jour qui a causé plus de mal que de bien. Cela est particulièrement important si vous travaillez sur des composants utilisés par plusieurs parties de votre application, comme des bases de données ou des caches.

4. Travailler sur le plus gros goulot d’étranglement d’abord

Et après avoir créé votre suite de tests et analysé votre application avec un profileur, vous disposez d’une liste des problèmes que vous souhaitez résoudre pour améliorer les performances. C’est bien, mais ça ne répond toujours pas à la question: où devriez-vous commencer? Vous pouvez vous concentrer sur les gains rapides ou commencer avec le problème le plus important.

Il pourrait être tentant de commencer avec les gains rapides, car vous serez en mesure de montrer les premiers résultats bientôt. Parfois, cela peut être nécessaire pour convaincre les autres membres de l’équipe ou votre direction que l’analyse de la performance en valait la peine.

Mais en général, je recommande de commencer en haut et de commencer à travailler sur le problème de performance le plus important en premier. Cela vous apportera la plus grande amélioration des performances et vous n’aurez peut-être pas besoin de résoudre plus de quelques-uns de ces problèmes pour répondre à vos exigences de performance.

Assez sur les conseils généraux de réglage des performances. Jetons un coup d’oeil à quelques unes spécifiques à Java.

5. Utilisez StringBuilder pour concaténer les chaînes par programme

Il y a beaucoup d’options différentes pour concaténer les String en Java. Vous pouvez, par exemple, utiliser un simple + ou + =, le bon vieux StringBuffer ou un StringBuilder.

Alors, quelle approche est recommandée?

La réponse dépend du code qui concatène la chaîne. Si vous ajoutez par programmation un nouveau contenu à votre chaîne, par exemple dans une boucle for, vous devez utiliser StringBuilder. Il est facile à utiliser et offre de meilleures performances que StringBuffer. Mais gardez à l’esprit que StringBuilder, contrairement à StringBuffer, n’est pas thread-safe et peut ne pas convenir à tous les cas d’utilisation.

Vous avez juste besoin d’instancier un nouveau StringBuilder et d’appeler la méthode append pour ajouter une nouvelle partie à la chaîne. Et lorsque vous avez ajouté toutes les parties, vous pouvez appeler la méthode toString() pour extraire la chaîne concaténée.

L’extrait de code suivant montre un exemple simple. A chaque itération, cette boucle convertit i en String et l’ajoute avec un espace à la StringBuilder sb. Donc, à la fin, ce code écrit « Ceci est un test0 1 2 3 4 5 6 7 8 9 » dans le fichier de log.

Comme vous pouvez le voir dans l’extrait de code, vous pouvez fournir le premier élément de votre chaîne à la méthode constructeur. Cela créera un nouveau StringBuilder contenant la chaîne fournie et une capacité de 16 caractères supplémentaires. Lorsque vous ajoutez plus de caractères à StringBuilder, votre JVM augmente dynamiquement la taille de StringBuilder.

Si vous connaissez déjà le nombre de caractères que votre chaîne contiendra, vous pouvez fournir ce nombre à une méthode constructeur différente pour instancier un StringBuilder avec la capacité définie. Cela améliore encore son efficacité car il n’a pas besoin d’étendre dynamiquement sa capacité.

6. Utilisez + pour concaténer les chaînes dans une seule instruction

Quand vous avez implémenté une première application en Java, quelqu’un vous a déjà probablement dit que vous ne pouvez pas enchaîner les String avec +. En effet, les String sont immuables et le résultat de chaque concaténation Chaîne est stocké dans un nouvel objet String. Cela requiert de la mémoire supplémentaire et ralentit considérablement votre application, en particulier si vous concaténez plusieurs chaînes dans une boucle.

Dans ce cas, suivez le conseil numéro 5 et utilisez un StringBuilder.

Mais ce n’est pas le cas si vous splittez une String en plusieurs lignes pour améliorer la lisibilité de votre code.

Dans ces situations, vous devez concaténer vos chaînes avec un +. Votre compilateur Java optimisera ceci et effectuera la concaténation au moment de la compilation. Ainsi, lors de l’exécution, votre code utilisera simplement 1 String, et aucune concaténation ne sera nécessaire.

7. Utiliser des primitives lorsque cela est possible

Un autre moyen simple et rapide permettant d’éviter toute surcharge et d’améliorer les performances de votre application consiste à utiliser des types primitifs au lieu de leurs classes wrapper. Donc, il est préférable d’utiliser un int au lieu d’un Integer, ou un double au lieu d’un Double. Cela permet à votre JVM de stocker la valeur dans la pile au lieu du tas pour réduire la consommation de mémoire et la gérer globalement plus efficacement.

8. Essayez d’éviter BigInteger et BigDecimal

Comme nous parlons déjà des types de données, nous devrions également jeter un coup d’œil à BigInteger et BigDecimal. Surtout ce dernier est populaire en raison de sa précision. Mais cela a un prix.

BigInteger et BigDecimal nécessitent beaucoup plus de mémoire qu’un simple long ou double et ralentissent considérablement tous les calculs. Donc, il faut réfléchir à deux fois avant de les utiliser (si vous avez besoin de la précision supplémentaire, ou si vos numéros dépassent la gamme d’un long). C’est peut-être la seule chose que vous devrez changer pour résoudre vos problèmes de performance, surtout si vous implémentez un algorithme mathématique.

9. Vérifiez d’abord le niveau log actuel

Cette recommandation devrait être évidente, mais malheureusement, vous pouvez trouver beaucoup de code qui l’ignore. Avant de créer un message de débogage, vous devez toujours vérifier le niveau de log en cours. Sinon, vous pouvez créer une String avec votre message de log qui sera ignoré par la suite.

Voici deux exemples à ne pas copier:

Dans les deux cas, vous allez effectuer toutes les étapes requises pour créer le message de log sans savoir si votre framework de log utilisera le message. Il est préférable de vérifier le niveau de log en cours avant de créer le message de débogage.

10. Utilisez Apache Commons StringUtils.Replace au lieu de String.replace

En général, la méthode String.replace fonctionne bien et est très efficace, surtout si vous utilisez Java 9. Mais si votre application nécessite un grand nombre d’opérations de remplacement et que vous n’avez pas mis à jour la version Java la plus récente, il est toujours logique vérifier des alternatives plus rapides et plus efficaces.

Un candidat est la méthode StringUtils.replace d’Apache Commons Lang. Comme l’a décrit Lukas Eder dans un article, il surpasse considérablement la méthode String.replace de Java 8.

Et cela nécessite juste un changement minime. Vous devez ajouter une dépendance Maven pour le projet Commons Lang d’Apache à votre application pom.xml et remplacer tous les appels de la méthode String.replace par la méthode StringUtils.replace.

11. Cache des ressources coûteuses, comme les connexions de base de données

La mise en cache est une solution populaire pour éviter l’exécution répétée d’extraits de code coûteux ou fréquemment utilisés. L’idée générale est simple: réutiliser de telles ressources coûte moins cher que d’en créer une nouvelle encore et encore.

Un exemple typique est la mise en cache des connexions de base de données dans un pool. La création d’une nouvelle connexion prend du temps, ce que vous pouvez éviter si vous réutilisez une connexion existante.

Vous pouvez également trouver d’autres exemples dans le langage Java lui-même. La méthode valueOf de la classe Integer, par exemple, met en cache les valeurs comprises entre -128 et 127. Vous pouvez dire que la création d’un nouvel entier n’est pas trop coûteuse, mais elle est si souvent utilisée que la mise en cache des valeurs les plus utilisées offre un avantage de performance certain.

Mais lorsque vous pensez à la mise en cache, gardez à l’esprit que votre implémentation de mise en cache crée également un surcoût. Vous devez dépenser de la mémoire supplémentaire pour stocker les ressources réutilisables, et vous devrez peut-être gérer votre cache pour rendre les ressources accessibles ou pour supprimer celles qui sont obsolètes.

Ainsi, avant de commencer à mettre en cache des ressources, veillez à les utiliser suffisamment souvent pour compenser le coût de votre implémentation de cache.

Résumé

Comme vous l’avez vu, cela ne demande pas beaucoup de travail pour améliorer les performances de votre application. La plupart des recommandations de ce post nécessitent juste un petit effort supplémentaire pour les appliquer à votre code.

Mais comme d’habitude, les recommandations les plus importantes sont indépendantes du langage:

  • Ne pas optimiser avant de savoir que c’est nécessaire
  • Utilisez un profileur pour trouver le véritable goulot d’étranglement
  • Travailler sur le plus gros goulot d’étranglement en premier

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.