jee - migration from java 8 to java 10




Les appels statiques Java sont-ils plus ou moins chers que les appels non statiques? (8)

Y a-t-il un avantage de performance d'une façon ou d'une autre? Est-ce spécifique au compilateur / VM? J'utilise Hotspot.


C'est un compilateur / VM spécifique.

  • En théorie , un appel statique peut être rendu légèrement plus efficace car il n'a pas besoin de faire une recherche de fonction virtuelle, et il peut également éviter le surcoût du paramètre "this" caché.
  • En pratique , de nombreux compilateurs optimiseront cela de toute façon.

Par conséquent, cela ne vaut probablement pas la peine d'être ennuyé, à moins que vous n'ayez identifié cela comme un problème de performance vraiment critique dans votre application. L'optimisation prématurée est la racine de tous les maux, etc ...

Cependant, j'ai vu cette optimisation donner une augmentation de performance substantielle dans la situation suivante:

  • Méthode effectuant un calcul mathématique très simple sans accès à la mémoire
  • Méthode invoquée des millions de fois par seconde dans une boucle interne serrée
  • Application liée à l'UC où chaque bit de performance importait

Si ce qui précède s'applique à vous, cela peut valoir la peine d'être testé.

Il y a aussi une autre raison valable (et potentiellement encore plus importante!) D'utiliser une méthode statique - si la méthode a réellement une sémantique statique (ie n'est pas connectée à une instance donnée de la classe), alors il est logique de la rendre statique pour refléter ce fait. Les programmeurs Java expérimentés remarqueront alors le modificateur statique et penseront immédiatement "aha! Cette méthode est statique, elle n'a donc pas besoin d'une instance et ne manipule vraisemblablement pas l'état spécifique de l'instance". Vous aurez donc communiqué la nature statique de la méthode de manière efficace ....


Comme le note Jon, les méthodes statiques ne peuvent pas être surchargées, donc l' appel simple d'une méthode statique peut être - sur un runtime Java suffisamment naïf - plus rapide que l' invocation d' une méthode d'instance.

Mais alors, même en supposant que vous êtes sur le point de perdre votre design pour sauver quelques nanosecondes, cela soulève une autre question: aurez-vous besoin d'une méthode qui vous écrase? Si vous changez votre code pour transformer une méthode d'instance en une méthode statique pour économiser une nanoseconde ici et là, puis que vous retournez et installez votre propre répartiteur, le vôtre sera presque certainement moins efficace que celui construit dans votre runtime Java déjà.


Eh bien, les appels statiques ne peuvent pas être remplacés (ils sont donc toujours candidats à l'inlining), et ne nécessitent aucune vérification de nullité. HotSpot fait un tas d'optimisations cool pour les méthodes d'exemple qui peuvent bien nier ces avantages, mais ils sont des raisons possibles pour lesquelles un appel statique peut être plus rapide.

Cependant, cela ne devrait pas affecter votre conception - le code de la façon la plus lisible et naturelle - et ne vous inquiétez de ce type de micro-optimisation que si vous avez une cause juste (ce que vous ne verrez presque jamais ).


En théorie, moins cher.

L'initialisation statique va se faire même si vous créez une instance de l'objet, alors que les méthodes statiques n'effectueront aucune initialisation normalement effectuée dans un constructeur.

Cependant, je n'ai pas testé cela.


Il pourrait y avoir une différence, et cela pourrait aller d'une façon ou d'une autre pour n'importe quel morceau de code particulier, et cela pourrait changer même avec une version mineure de la JVM.

Cela fait certainement partie des shreevatsa.wordpress.com/2008/05/16/… .


Je voudrais ajouter aux autres bonnes réponses ici que cela dépend aussi de votre flux, par exemple:

Public class MyDao {

   private String sql = "select * from MY_ITEM";

   public List<MyItem> getAllItems() {
       springJdbcTemplate.query(sql, new MyRowMapper());
   };
};

Faites attention à créer un nouvel objet MyRowMapper pour chaque appel.
Au lieu de cela, je suggère d'utiliser ici un champ statique.

Public class MyDao {

   private static RowMapper myRowMapper = new MyRowMapper();
   private String sql = "select * from MY_ITEM";

   public List<MyItem> getAllItems() {
       springJdbcTemplate.query(sql, myRowMapper);
   };
};

Premièrement: vous ne devriez pas faire le choix entre statique et non statique sur la base de la performance.

Deuxièmement: en pratique, cela ne fera aucune différence. Hotspot peut choisir d'optimiser de manière à rendre les appels statiques plus rapides pour une méthode, les appels non statiques plus rapidement pour un autre.

Troisièmement: une grande partie des mythes entourant statique et non statique sont basés soit sur de très vieilles machines virtuelles Java (qui ne se rapprochaient pas de l'optimisation faite par Hotspot), soit sur des informations triviales sur C ++ (dans lesquelles un appel dynamique utilise un accès mémoire supplémentaire) qu'un appel statique).


7 ans plus tard ...

Je n'ai pas un très grand degré de confiance dans les résultats que Mike Nakis a trouvés parce qu'ils ne traitent pas de problèmes courants liés aux optimisations de Hotspot. J'ai instrumenté des benchmarks en utilisant JMH et j'ai trouvé que la surcharge d'une méthode d'instance était d'environ 0.75% sur ma machine par rapport à un appel statique. Compte tenu de cette faible surcharge, je pense que, sauf dans les opérations les plus sensibles à la latence, ce n'est sans doute pas la plus grande préoccupation dans la conception d'applications. Les résultats sommaires de mon indice de référence JMH sont les suivants:

java -jar target/benchmark.jar

# -- snip --

Benchmark                        Mode  Cnt          Score         Error  Units
MyBenchmark.testInstanceMethod  thrpt  200  414036562.933 ± 2198178.163  ops/s
MyBenchmark.testStaticMethod    thrpt  200  417194553.496 ± 1055872.594  ops/s

Vous pouvez regarder le code ici sur Github;

https://github.com/nfisher/svsi

Le benchmark lui-même est assez simple mais vise à minimiser l'élimination du code mort et le repliement constant. Il y a probablement d'autres optimisations que j'ai manqué / négligé et ces résultats sont susceptibles de varier selon la version et le système d'exploitation de la JVM.

package ca.junctionbox.svsi;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;

class InstanceSum {
    public int sum(final int a, final int b) {
        return a + b;
    }
}

class StaticSum {
    public static int sum(final int a, final int b) {
        return a + b;
    }
}

public class MyBenchmark {
    private static final InstanceSum impl = new InstanceSum();

    @State(Scope.Thread)
    public static class Input {
        public int a = 1;
        public int b = 2;
    }

    @Benchmark
    public void testStaticMethod(Input i, Blackhole blackhole) {
        int sum = StaticSum.sum(i.a, i.b);
        blackhole.consume(sum);
    }

    @Benchmark
    public void testInstanceMethod(Input i, Blackhole blackhole) {
        int sum = impl.sum(i.a, i.b);
        blackhole.consume(sum);
    }
}




premature-optimization