11
votes

Java - Strings est égal à la décompilation

J'ai décompilé du code Java l'autre jour et j'ai trouvé ceci: xxx

utilisé évidemment en utilisant "==" pour tester l'égalité des chaînes est mauvais

mais je me suis demandé - Ce code a été compilé et décompilé. Si toutes les chaînes ont été définies lors de la compilation et internées et que le code a été compilé - est-il possible que S1.Equals (S2) ait pu être optimisé vers 'S1 == S2'? < / p>


6 commentaires

Une façon serait de compiler un code comme ça, et de voir ce qui revient du décompiler :)


De quel code compilé a-t-il été décompilé?


En supposant que vous utilisiez la même version du compilateur qu'ils ont utilisé pour créer le fichier de classe d'origine.


Cela ressemble à une "optimisation" que le programmeur aurait effectué (correctement ou incorrectement), et non que le compilateur aurait effectué.


Pourquoi voudriez-vous faire cela? String # Equals utilise == comme premier test déjà.


@dasblinkenlight Je me demandais si l'un des compilateurs le ferait: par exemple aussi bien que le compilateur Oracle / Sun, OpenJDK, JRocket, etc.


3 Réponses :


1
votes

Non, il ne ressemble pas à Java l'optimise cette option (par défaut).

Je viens de comparer les deux solutions. S'il est non optimisé, nous nous attendons à voir s1.equals (S2) plus lentement que S1 == S2 . C'est exactement ce que nous voyons. S'il était optimisé, alors s1.equals (S2) prendrait la même quantité de temps que S1 == S2 . Cependant, ils prennent des quantités différentes (de l'ordre de 50 000 nanosecondes). Ceci est pas une mesure directe de cette compilation, mais c'est une inférence raisonnable.

La raison pour laquelle cela ne sera pas optimisé pour == est dû au fait que l'opérateur égal, pour les objets, comparera l'adresse de mémoire d'objet, pas le contenu de l'objet lui-même. Donc, si vous changez S1 , alors, si le compilateur a optimisé cela, vous modifiez également s2 .

Cependant, cela risque de casser le code, le compilateur ne le fera pas. Il laissera les adresses de mémoire de S1 et s2 .


7 commentaires

@RAPTODOTCPP Je pense que c'était tactique. Tant pis.


Je suis légèrement surpris que le Jit n'oblise pas cela. Avez-vous écrit une référence judicieuse avec l'échauffement, la propre méthode, etc.?


@Voo Pour être honnête, c'était plus rapide et sale. Moyenne de quelques centaines de milliers de systèmes.NanOtime (). Cependant, c'est assez précis de cette façon de toute façon. Et je ne suis pas surpris que ce n'est pas optimisé (voir ma réponse).


@Telthien Votre explication n'a pas de sens que je crains, car les cordes sont immuables. Aussi longtemps que nous avons s1 = "foo" et aucune attribution ultérieure à S1 dans cette méthode , le compilateur peut être sûr qu'il connaît la startaddresse du tampon de charme.


@Voo Voir la réponse ci-dessus: Ce n'est pas optimisé.


@Telthien La réponse ci-dessus répond aux réponses pour Java bytecode , pas le code généré par JIT.


@Voo hmm. Je ne suis pas sûr alors.



8
votes

Je doute fortement. En règle générale, les compilateurs Java font très peu à titre d'optimisation bytecode, laissant l'optimisation de la phase JIT.

J'ai expérimenté cela un peu, et mon compilateur ne fait rien d'intéressant avec ce qui suit: P >

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #16                 // String something
       2: astore_1      
       3: ldc           #18                 // String something_else
       5: astore_2      
       6: ldc           #16                 // String something
       8: ldc           #18                 // String something_else
      10: invokevirtual #20                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      13: ifeq          27
      16: getstatic     #26                 // Field java/lang/System.out:Ljava/io/PrintStream;
      19: ldc           #32                 // String yes
      21: invokevirtual #34                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      24: goto          35
      27: getstatic     #26                 // Field java/lang/System.out:Ljava/io/PrintStream;
      30: ldc           #40                 // String no
      32: invokevirtual #34                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      35: return        


0 commentaires

0
votes

La règle principale est que si le compilateur pouvait déduire une valeur exacte du code source, elle a dans la classe unique. Parce que toutes les optimisations n'utilisent que la plus petite unité de compilation. Si j'écris un code xxx

Le compilateur voit tous les codes liés à l'instruction dans cette classe et optimise la condition IF. Après un compilateur, le code ressemble à xxx

(j'ai utilisé JD-GUI )

Cependant, si vous remplacez == par le .fequals , le compilateur ne peut pas Supposons comment la méthode angles fonctionne. Parce que, après la compilation de la classe Test , vous pouvez pirater votre JDK et placer une autre version de la classe java.lang.string qui retourne true pour "1" .equals ("2") .

Ainsi, réfléchissez à l'optimisation du compilateur pouvant faire le compilateur, d'abord penser à quel point le compilateur pourrait se compiler si une classe pouvait être recompanée si une classe pouvait être recompanée. Plus tard.

Comme autre exemple, vous pouvez voir Comment Enum est implémenté et pourquoi a-t-il besoin d'une manière "étrange".


0 commentaires