La création d'un objet à l'aide de la réflexion plutôt que d'appeler le constructeur de classe entraîne-t-elle des différences de performances significatives?
java
performance
optimization
reflection
dmanxiii
la source
la source
Réponses:
Oui absolument. Rechercher une classe par réflexion est, par ampleur , plus cher.
Citant la documentation de Java sur la réflexion :
Voici un test simple que j'ai piraté en 5 minutes sur ma machine, exécutant Sun JRE 6u10:
Avec ces résultats:
Gardez à l'esprit que la recherche et l'instanciation sont effectuées ensemble, et dans certains cas, la recherche peut être refactorisée, mais ce n'est qu'un exemple basique.
Même si vous venez d'instancier, vous obtenez toujours un impact sur les performances:
Encore une fois, YMMV.
la source
Oui, c'est plus lent.
Mais rappelez-vous la putain de règle n ° 1 - L'OPTIMISATION PRÉMATURE EST LA ROOT DE TOUT MAL
(Eh bien, peut être à égalité avec le n ° 1 pour DRY)
Je jure que si quelqu'un s'approchait de moi au travail et me demandait cela, je serais très attentif à leur code pour les prochains mois.
Vous ne devez jamais optimiser jusqu'à ce que vous soyez sûr d'en avoir besoin, jusque-là, écrivez simplement du bon code lisible.
Oh, et je ne veux pas non plus écrire du code stupide. Pensez simplement à la manière la plus propre possible de le faire - pas de copier-coller, etc. (Méfiez-vous toujours de choses comme les boucles internes et utilisez la collection qui correspond le mieux à vos besoins - Ignorer ce n'est pas une programmation «non optimisée» , c'est une "mauvaise" programmation)
Cela me fait peur quand j'entends des questions comme celle-ci, mais j'oublie que tout le monde doit apprendre toutes les règles lui-même avant de vraiment comprendre. Vous l'obtiendrez après avoir passé un mois-homme à déboguer quelque chose de "Optimisé".
ÉDITER:
Une chose intéressante s'est produite dans ce fil. Vérifiez la réponse n ° 1, c'est un exemple de la puissance du compilateur pour optimiser les choses. Le test est complètement invalide car l'instanciation non réfléchissante peut être complètement éliminée.
Leçon? N'optimisez JAMAIS jusqu'à ce que vous ayez écrit une solution propre et soigneusement codée et que vous n'ayez pas prouvé qu'elle était trop lente.
la source
Vous pouvez constater que A a = new A () est en cours d'optimisation par la JVM. Si vous placez les objets dans un tableau, ils ne fonctionnent pas très bien. ;) Les impressions suivantes ...
Cela suggère que la différence est d'environ 150 ns sur ma machine.
la source
Class.getDeclaredMethod
) et que j'appelle ensuiteMethod.invoke
plusieurs fois? Est-ce que j'utilise la réflexion une ou autant de fois que je l'invoque? Question de suivi, que se passe-t-il si au lieu deMethod
cela, c'est unConstructor
et je le faisConstructor.newInstance
plusieurs fois?S'il y a vraiment besoin de quelque chose de plus rapide que la réflexion, et qu'il ne s'agit pas simplement d'une optimisation prématurée, la génération de bytecode avec ASM ou une bibliothèque de niveau supérieur est une option. Générer le bytecode la première fois est plus lent que d'utiliser simplement la réflexion, mais une fois que le bytecode a été généré, il est aussi rapide que le code Java normal et sera optimisé par le compilateur JIT.
Quelques exemples d'applications qui utilisent la génération de code:
L'appel de méthodes sur des proxys générés par CGLIB est légèrement plus rapide que les proxys dynamiques de Java , car CGLIB génère du bytecode pour ses proxys, mais les proxys dynamiques utilisent uniquement la réflexion ( j'ai mesuré CGLIB pour être environ 10x plus rapide dans les appels de méthode, mais la création des proxies était plus lente).
JSerial génère du bytecode pour lire / écrire les champs des objets sérialisés, au lieu d'utiliser la réflexion. Il y a quelques benchmarks sur le site de JSerial.
Je ne suis pas sûr à 100% (et je n'ai pas envie de lire la source maintenant), mais je pense que Guice génère du bytecode pour faire l'injection de dépendances. Corrige moi si je me trompe.
la source
«Significatif» dépend entièrement du contexte.
Si vous utilisez la réflexion pour créer un objet de gestionnaire unique basé sur un fichier de configuration, puis que vous passez le reste de votre temps à exécuter des requêtes de base de données, cela est insignifiant. Si vous créez un grand nombre d'objets par réflexion dans une boucle serrée, alors oui, c'est important.
En général, la flexibilité de conception (le cas échéant!) Devrait conduire votre utilisation de la réflexion, pas des performances. Cependant, pour déterminer si les performances sont un problème, vous devez établir un profil plutôt que d'obtenir des réponses arbitraires d'un forum de discussion.
la source
Il y a une certaine surcharge avec la réflexion, mais c'est beaucoup plus petit sur les machines virtuelles modernes qu'auparavant.
Si vous utilisez la réflexion pour créer chaque objet simple dans votre programme, quelque chose ne va pas. L'utiliser occasionnellement, quand vous avez de bonnes raisons, ne devrait pas du tout être un problème.
la source
Oui, il y a un impact sur les performances lors de l'utilisation de Reflection, mais une solution de contournement possible pour l'optimisation consiste à mettre la méthode en cache:
aura pour résultat:
[java] L'appel de la méthode 1000000 fois par réflexe avec recherche a pris 5618 millis
[java] L'appel de la méthode 1000000 fois par réflexe avec cache a pris 270 millis
la source
La réflexion est lente, bien que l'allocation d'objets ne soit pas aussi désespérée que d'autres aspects de la réflexion. Pour obtenir des performances équivalentes avec l'instanciation basée sur la réflexion, vous devez écrire votre code afin que le jit puisse dire quelle classe est instanciée. Si l'identité de la classe ne peut pas être déterminée, le code d'allocation ne peut pas être intégré. Pire encore, l'analyse d'échappement échoue et l'objet ne peut pas être alloué par pile. Si vous avez de la chance, le profilage d'exécution de la JVM peut venir à la rescousse si ce code devient chaud, et peut déterminer dynamiquement quelle classe prédomine et peut optimiser pour celle-là.
Sachez que les microbenchmarks de ce fil sont profondément imparfaits, alors prenez-les avec un grain de sel. Le moins imparfait est de loin celui de Peter Lawrey: il effectue des cycles de préchauffage pour obtenir les méthodes, et il défait (consciemment) l'analyse des évasions pour s'assurer que les allocations se produisent réellement. Même celui-là a ses problèmes, cependant: par exemple, on peut s'attendre à ce que le nombre énorme de magasins de baies vaincre les caches et les tampons de stockage, donc cela finira par être principalement une référence de mémoire si vos allocations sont très rapides. (Félicitations à Peter pour avoir bien conclu: que la différence est "150ns" plutôt que "2,5x". Je soupçonne qu'il fait ce genre de chose pour gagner sa vie.)
la source
Il est intéressant de noter que le paramètre setAccessible (true), qui ignore les contrôles de sécurité, a une réduction de coût de 20%.
Sans setAccessible (true)
Avec setAccessible (true)
la source
1000000
appels?setAccessible()
peut avoir beaucoup plus de différences en général, en particulier pour les méthodes avec plusieurs arguments, il doit donc toujours être appelé.Oui, c'est beaucoup plus lent. Nous exécutions du code qui faisait cela, et bien que je n'ai pas les métriques disponibles pour le moment, le résultat final a été que nous avons dû refactoriser ce code pour ne pas utiliser la réflexion. Si vous savez ce qu'est la classe, appelez simplement le constructeur directement.
la source
Dans le doReflection (), il y a la surcharge due à Class.forName ("misc.A") (qui nécessiterait une recherche de classe, analysant potentiellement le chemin de classe sur le système de fils), plutôt que le newInstance () appelé sur la classe. Je me demande à quoi ressembleraient les statistiques si le Class.forName ("misc.A") n'est fait qu'une seule fois en dehors de la boucle for, il n'est pas vraiment nécessaire de le faire pour chaque invocation de la boucle.
la source
Oui, il sera toujours plus lent de créer un objet par réflexion car la JVM ne peut pas optimiser le code lors de la compilation. Consultez les didacticiels Sun / Java Reflection pour plus de détails.
Voir ce test simple:
la source
Class.forName()
) de l'instanciation (newInstance ()), car leurs caractéristiques de performances varient considérablement et vous pouvez parfois éviter la recherche répétée dans un système bien conçu.Souvent, vous pouvez utiliser Apache commons BeanUtils ou PropertyUtils qui introspection (en gros, ils mettent en cache les métadonnées sur les classes afin qu'ils n'aient pas toujours besoin d'utiliser la réflexion).
la source
Je pense que cela dépend de la légèreté / lourdeur de la méthode cible. si la méthode cible est très légère (par exemple getter / setter), elle pourrait être 1 ~ 3 fois plus lente. si la méthode cible prend environ 1 milliseconde ou plus, les performances seront très proches. voici le test que j'ai fait avec Java 8 et reflectasm :
Le code de test complet est disponible sur GitHub: ReflectionTest.java
la source