web-dev-qa-db-fra.com

Pourquoi / quand vous ne voudriez pas que Java 8 UseStringDeduplication soit activé dans JVM?

Java 8 a introduit la déduplication des chaînes qui peut être activée en lançant JVM avec -XX:+UseStringDeduplication option permettant d'économiser de la mémoire en référençant des objets String similaires au lieu de conserver des doublons. Bien sûr, son efficacité varie d'un programme à l'autre en fonction de l'utilisation de Strings mais je pense qu'il est sûr de dire qu'en général, il peut être considéré comme bénéfique pour la plupart des applications (sinon toutes), ce qui me fait me poser des questions sur peu de choses:

Pourquoi n'est-il pas activé par défaut? Est-ce à cause des coûts associés à la déduplication ou simplement parce que G1GC est toujours considéré comme nouveau?

Existe-t-il (ou pourrait-il y avoir) des cas Edge où vous ne souhaitez pas utiliser la déduplication?

22
Ninetou

Les cas où la déduplication de chaîne pourrait être nuisible incluent:

  • Il y a beaucoup de chaînes mais une très faible probabilité de doublons: le temps de recherche de doublons et l'espace de la structure de déduplication ne seraient pas remboursés.
  • Il existe une probabilité raisonnable de doublons, mais la plupart des chaînes meurent en quelques cycles de GC1 en tous cas. La déduplication est moins bénéfique si les chaînes déduitées devaient être GC'ed bientôt de toute façon.

    (Il ne s'agit pas de chaînes qui ne survivent pas au premier cycle de GC. Cela n'aurait aucun sens pour le GC de même essayer de déduper les chaînes qu'il sait être des ordures.)

Nous ne pouvons que spéculer sur les raisons pour lesquelles l'équipe Java Java n'a pas activé la déduplication par défaut, mais elle est bien mieux placée pour prendre des décisions rationnelles (c'est-à-dire basées sur des preuves) à ce sujet qui vous et moi. D'après ce que je comprends, ils ont accès à de nombreuses grandes applications du monde réel pour comparer/tester les effets des optimisations. Ils peuvent également avoir des contacts dans des organisations partenaires ou clientes avec des bases de code similaires et des préoccupations d'efficacité .. . à qui peut-il demander des commentaires pour savoir si les optimisations dans une version à accès anticipé fonctionnent comme prévu.

1 - Cela dépend de la valeur du paramètre JVM StringDeduplicationAgeThreshold. La valeur par défaut est 3, ce qui signifie que (approximativement) une chaîne doit survivre à 3 collections mineures ou à une collection majeure pour être prise en compte pour la déduplication. Mais de toute façon, si une chaîne est dédupliquée et ensuite jugée inaccessible peu de temps après, les frais généraux de déduplication ne seront pas remboursés pour cette chaîne.


Si vous demandez quand vous devriez envisager d'activer la déduplication, mon conseil serait de l'essayer et de voir si cela aide par application . Mais vous devez faire un benchmark au niveau de l'application (ce qui demande des efforts!) Pour être sûr que la déduplication est bénéfique ...

Une lecture attentive de JEP 192 vous aiderait également à comprendre les problèmes et à juger de la façon dont ils pourraient s'appliquer à votre Java application.

25
Stephen C

Je comprends absolument que cela ne répond pas à la question , je voulais juste mentionner que jdk-9 introduit une autre optimisation activée par défaut appelée:

-XX: + CompactStrings

Latin1 caractères occupent un seul octet au lieu de deux (via un caractère). En raison de ce changement, de nombreuses méthodes internes de String ont changé - elles agissent de même pour l'utilisateur, mais en interne, elles sont plus rapides dans de nombreux cas.

De plus, dans le cas de chaînes pour concaténer deux chaînes ensemble via le signe plus, le javac va générer un bytecode différent.

Il n'y a pas d'instruction de bytecode qui concatène deux chaînes ensemble afin que le javac génère un

StringBuilder # append

dans le back-end. Jusqu'au jdk-9.

Maintenant, le bytecode délègue à

StringConcatFactory # makeConcatWithConstants

ou

StringConcatFactory # makeConcat

via l'instruction de bytecode dynamique invoqué:

   aload_0
   1: aload_2
   2: aload_1
   3: invokedynamic #8,  0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
   8: areturn 

La façon dont les deux chaînes sont concaténées est désormais une décision d'exécution. il peut s'agir d'un StringBuilder ou d'une concaténation de tableaux d'octets, etc. Tout ce que vous savez, c'est que cela peut changer et vous obtiendrez la solution la plus rapide possible.

MODIFIER

Je viens de déboguer et j'ai vu qu'il existe de nombreuses stratégies pour ajouter ces chaînes:

    private enum Strategy {
    /**
     * Bytecode generator, calling into {@link Java.lang.StringBuilder}.
     */
    BC_SB,

    /**
     * Bytecode generator, calling into {@link Java.lang.StringBuilder};
     * but trying to estimate the required storage.
     */
    BC_SB_SIZED,

    /**
     * Bytecode generator, calling into {@link Java.lang.StringBuilder};
     * but computing the required storage exactly.
     */
    BC_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that in the end calls into {@link Java.lang.StringBuilder}.
     * This strategy also tries to estimate the required storage.
     */
    MH_SB_SIZED,

    /**
     * MethodHandle-based generator, that in the end calls into {@link Java.lang.StringBuilder}.
     * This strategy also estimate the required storage exactly.
     */
    MH_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that constructs its own byte[] array from
     * the arguments. It computes the required storage exactly.
     */
    MH_INLINE_SIZED_EXACT
}

La valeur par défaut étant:

MH_INLINE_SIZED_EXACT

18
Eugene