Vous semblez poser deux questions assez différentes:
- Java est-il vraiment lent, et si oui, pourquoi?
- Pourquoi Java est-il perçu comme lent, même s'il est plus rapide que de nombreuses alternatives?
La première de ces questions est plus ou moins une question du genre «combien de temps dure une corde». Cela revient à votre définition de «lent». Comparé à un interpréteur pur, Java est extrêmement rapide. Comparé à d'autres langages qui sont (normalement) compilés en une sorte de bytecode, puis compilés dynamiquement en code machine (par exemple C # ou autre chose sur .NET), Java est à peu près sur un pied d'égalité. Comparé aux langages qui sont normalement compilés en code machine pur, et qui ont (souvent de grandes) équipes de personnes qui ne travaillent que sur l'amélioration de leurs optimiseurs (par exemple C, C ++, Fortran, Ada), Java fait plutôt bien sur un certain nombre de choses, mais dans l'ensemble a tendance à être au moins un peu plus lent.
Une grande partie de cela est principalement liée à l'implémentation - fondamentalement, cela se résume au fait qu'un utilisateur attend pendant qu'un compilateur dynamique / JIT s'exécute, donc à moins que vous n'ayez un programme qui s'exécute pendant un certain temps pour commencer, c'est difficile de justifier que le compilateur passe beaucoup de temps sur des optimisations difficiles. Par conséquent, la plupart des compilateurs Java (et C #, etc.) ne mettent pas beaucoup d'efforts dans des optimisations vraiment difficiles. Dans de nombreux cas, il s'agit moins de savoir quelles optimisations sont effectuées que de savoir où elles sont appliquées. De nombreux problèmes d'optimisation sont NP complets, de sorte que le temps qu'ils prennent augmente rapidement avec la taille du problème attaqué. Une façon de garder le temps dans la raison est de n'appliquer l'optimisation qu'à quelque chose comme une seule fonction à la fois. Quand il n'y a que le développeur qui attend le compilateur, vous pouvez vous permettre de prendre beaucoup plus de temps et d'appliquer la même optimisation à des morceaux beaucoup plus volumineux du programme. De même, le code de certaines optimisations est assez poilu (et peut donc être assez gros). Encore une fois, comme l'utilisateur attend pendant le chargement de ce code (et le temps de démarrage de la JVM est souvent un facteur important dans le temps global), la mise en œuvre doit équilibrer le temps gagné à un endroit par rapport à la perte à un autre - et compte tenu du peu de code bénéficie des optimisations épineuses, il est généralement plus avantageux de garder la JVM petite.
Un deuxième problème est qu'avec Java, vous obtenez souvent une solution plus ou moins «universelle». Par exemple, pour de nombreux développeurs Java, Swing est essentiellement la seule bibliothèque de fenêtrage disponible. Dans quelque chose comme C ++, il existe littéralement des dizaines de bibliothèques de fenêtrage, de frameworks d'application, etc., chacun avec son propre ensemble de compromis entre facilité d'utilisation vs exécution rapide, aspect cohérent vs aspect natif, etc. Le seul vrai problème est que certains (par exemple Qt) peuvent être assez chers (au moins pour un usage commercial).
Troisièmement, beaucoup de code écrit en C ++ (et encore plus en C) est simplement plus ancien et plus mature. Beaucoup de ceux-ci contiennent un noyau de routines écrites il y a des décennies, alors que passer plus de temps à optimiser le code était un comportement normal et attendu. Cela a souvent un réel avantage dans un code plus petit et plus rapide. C ++ (ou C) obtient le crédit pour le code étant petit et rapide, mais c'est vraiment beaucoup plus un produit du développeur et des contraintes du moment où le code a été écrit. Dans une certaine mesure, cela conduit à une prophétie auto-réalisatrice - lorsque les gens se soucient de la vitesse, ils choisissent souvent C ++ parce qu'il a cette réputation. Ils consacrent plus de temps et d'efforts à l'optimisation et une nouvelle génération de code C ++ rapide est écrite.
Pour résumer, l'implémentation normale de Java rend au mieux une optimisation maximale problématique. Pire encore, là où Java est visible , des éléments tels que les boîtes à outils de fenêtrage et le temps de démarrage de la JVM jouent souvent un rôle plus important que la vitesse d'exécution du langage lui-même de toute façon. Dans de nombreux cas, C et C ++ obtiennent également du crédit pour ce qui est vraiment le produit de simplement travailler plus dur à l'optimisation.
Quant à la deuxième question, je pense que c'est en grande partie une question de nature humaine au travail. Quelques fanatiques font des déclarations plutôt exagérées sur la rapidité aveuglante de Java. Quelqu'un l'essaie et constate que même un programme trivial prend quelques secondes pour démarrer, et se sent lent et maladroit lorsqu'il s'exécute. Peu de gens prennent probablement la peine d'analyser les choses pour se rendre compte qu'une grande partie de cela est le temps de démarrage de la JVM, et le fait que lorsqu'ils essaient pour la première fois, aucun code n'a encore été compilé - une partie du code est en cours d'interprétation, et certains sont compilés en attendant. Pire encore, même s'il fonctionne assez vite, l'aspect et la convivialité sembleront généralement étrangers et maladroits à la plupart des utilisateurs, donc même si des mesures objectives montraient des temps de réponse rapides, cela semblerait toujours maladroit.
Les ajouter ensemble conduit à une réaction assez simple et naturelle: que Java est lent, laid et maladroit. Étant donné le battage médiatique qui dit que c'est vraiment rapide, il y a une tendance à réagir de manière excessive et à conclure que c'est horriblement lent, au lieu d'un (plus précis) "légèrement plus lent, et cela surtout dans des circonstances spécifiques." C'est généralement le pire pour un développeur qui écrit les premiers programmes dans le langage. L'exécution d'un programme "hello world" dans la plupart des langages semble instantanée, mais en Java, il y a une pause facilement perceptible au démarrage de la JVM. Même un interpréteur pur qui fonctionne beaucoup plus lentement sur des boucles serrées et qui apparaîtra souvent plus vite pour un code comme celui-ci, simplement parce qu'il peut être chargé et commencer à s'exécuter un peu plus tôt.