Y a-t-il des frais généraux lorsque nous convertissons des objets d'un type à un autre? Ou le compilateur résout tout simplement et il n'y a aucun coût au moment de l'exécution?
S'agit-il de choses générales ou existe-t-il des cas différents?
Par exemple, supposons que nous ayons un tableau de Object [], où chaque élément peut avoir un type différent. Mais nous savons toujours avec certitude que, disons, l'élément 0 est un double, l'élément 1 est une chaîne. (Je sais que c'est une mauvaise conception, mais supposons simplement que je devais le faire.)
Les informations de type de Java sont-elles toujours conservées au moment de l'exécution? Ou tout est oublié après la compilation, et si nous faisons des éléments (Double) [0], nous suivrons simplement le pointeur et interpréterons ces 8 octets comme un double, quoi que ce soit?
Je ne sais pas très bien comment les types sont faits en Java. Si vous avez des recommandations sur des livres ou des articles, merci aussi.
Réponses:
Il existe 2 types de casting:
Conversion implicite , lorsque vous transtypez d'un type vers un type plus large, ce qui se fait automatiquement et il n'y a pas de surcharge:
Casting explicite , lorsque vous passez d'un type plus large à un type plus étroit. Dans ce cas, vous devez explicitement utiliser la diffusion comme celle-ci:
Dans ce deuxième cas, il y a une surcharge au moment de l'exécution, car les deux types doivent être vérifiés et au cas où la conversion ne serait pas possible, JVM doit lever une ClassCastException.
Tiré de JavaWorld: le coût du casting
la source
((String)o).someMethodOfCastedClass()
Pour une implémentation raisonnable de Java:
Chaque objet a un en-tête contenant, entre autres, un pointeur vers le type d'exécution (par exemple
Double
ouString
, mais il ne pourrait jamais êtreCharSequence
ouAbstractList
). En supposant que le compilateur d'exécution (généralement HotSpot dans le cas de Sun) ne peut pas déterminer le type de manière statique, une vérification doit être effectuée par le code machine généré.Tout d'abord, ce pointeur vers le type d'exécution doit être lu. Ceci est de toute façon nécessaire pour appeler une méthode virtuelle dans une situation similaire.
Pour la conversion en un type de classe, on sait exactement combien de superclasses il y a jusqu'à ce que vous frappiez
java.lang.Object
, de sorte que le type peut être lu à un décalage constant par rapport au pointeur de type (en fait les huit premières dans HotSpot). Encore une fois, cela revient à lire un pointeur de méthode pour une méthode virtuelle.Ensuite, la valeur de lecture a juste besoin d'une comparaison avec le type statique attendu de la distribution. En fonction de l'architecture du jeu d'instructions, une autre instruction devra brancher (ou faire une erreur) sur une branche incorrecte. Les ISA tels que ARM 32 bits ont une instruction conditionnelle et peuvent être capables de faire passer le triste chemin par le chemin heureux.
Les interfaces sont plus difficiles en raison de l'héritage multiple d'interface. Généralement, les deux derniers transtypages vers les interfaces sont mis en cache dans le type d'exécution. AU tout début (il y a plus de dix ans), les interfaces étaient un peu lentes, mais ce n'est plus pertinent.
J'espère que vous pouvez voir que ce genre de chose est largement sans rapport avec les performances. Votre code source est plus important. En termes de performances, le plus gros succès de votre scénario est susceptible d'être des échecs de cache dus à la poursuite des pointeurs d'objet partout (les informations de type seront bien sûr communes).
la source
Le compilateur ne note pas les types des éléments individuels d'un tableau. Il vérifie simplement que le type de chaque expression d'élément est attribuable au type d'élément de tableau.
Certaines informations sont conservées au moment de l'exécution, mais pas les types statiques des éléments individuels. Vous pouvez voir cela en regardant le format de fichier de classe.
Il est théoriquement possible que le compilateur JIT utilise une "analyse d'échappement" pour éliminer les vérifications de type inutiles dans certaines affectations. Cependant, faire cela dans la mesure que vous suggérez serait au-delà des limites d'une optimisation réaliste. Le gain de l'analyse des types d'éléments individuels serait trop faible.
De plus, les gens ne devraient pas écrire de code d'application comme ça de toute façon.
la source
(float) Math.toDegrees(theta)
Y aura-t-il des frais généraux importants ici également?L'instruction de code d'octet pour effectuer la conversion au moment de l'exécution est appelée
checkcast
. Vous pouvez désassembler le code Java en utilisantjavap
pour voir quelles instructions sont générées.Pour les tableaux, Java conserve les informations de type au moment de l'exécution. La plupart du temps, le compilateur détecte les erreurs de type pour vous, mais il y a des cas où vous rencontrerez un
ArrayStoreException
en essayant de stocker un objet dans un tableau, mais le type ne correspond pas (et le compilateur ne l'a pas détecté) . La spécification du langage Java donne l'exemple suivant:Point[] pa = cpa
est valide carColoredPoint
est une sous-classe de Point, maispa[0] = new Point()
n'est pas valide.Ceci est opposé aux types génériques, où aucune information de type n'est conservée lors de l'exécution. Le compilateur insère des
checkcast
instructions si nécessaire.Cette différence de typage pour les types génériques et les tableaux rend souvent inadapté le mélange de tableaux et de types génériques.
la source
En théorie, des frais généraux sont introduits. Cependant, les JVM modernes sont intelligentes. Chaque implémentation est différente, mais il n'est pas déraisonnable de supposer qu'il pourrait exister une implémentation que JIT a optimisée en jetant les vérifications alors qu'elle pourrait garantir qu'il n'y aurait jamais de conflit. Quant aux JVM spécifiques qui proposent cela, je ne pourrais pas vous le dire. Je dois admettre que j'aimerais moi-même connaître les spécificités de l'optimisation JIT, mais ce sont les ingénieurs JVM qui doivent se soucier.
La morale de l'histoire est d'écrire d'abord un code compréhensible. Si vous rencontrez des ralentissements, profilez et identifiez votre problème. Il y a de bonnes chances que ce ne soit pas dû au casting. Ne sacrifiez jamais du code propre et sûr dans le but de l'optimiser JUSQU'À CE QUE VOUS SAVEZ QUE VOUS DEVEZ LE FAIRE.
la source