Почему/когда вы не хотите использовать Java 8 UseStringDeduplication в JVM?

В Java 8 введена дедупликация строк, которая может быть включена путем запуска JVM с опцией -XX:+UseStringDeduplication, позволяющей сэкономить некоторую память, ссылаясь на аналогичные объекты String, а не на дублирование. Конечно, эффективность варьируется от программы к программе в зависимости от использования Strings, но я думаю, что можно с уверенностью сказать, что в целом это может считаться полезным для большинства приложений (если не для всех), заставляя меня задуматься о нескольких вещах:

Почему он не включен по умолчанию? Это из-за затрат, связанных с дедупликации или просто потому, что G1GC по-прежнему считается новым?

Существуют (или могут быть) какие-либо граничные случаи, когда вы не хотите использовать дедупликацию?

Ответ 1

Случаи, когда дедупликация строк может быть вредной, включают в себя:

  • Есть много строк, но очень низкая вероятность дубликатов: временные затраты на поиск дубликатов и пространственные издержки структуры данных дедупликации не будут возвращены.
  • Существует разумная вероятность дубликатов, но большинство строк все равно умирает в течение пары циклов GC 1. Дедупликация менее выгодна, если в любом случае скоро будут удалены GD'-дуплицированные строки.

    (Речь идет не о строках, которые не выдерживают первый цикл GC. Для GC не имеет смысла даже пытаться де-дупилировать строки, которые, как он знает, являются мусором.)

Мы можем только строить догадки относительно того, почему команда Java не включила дедупликацию по умолчанию, но они в гораздо лучшем положении, чтобы принимать рациональные (то есть основанные на доказательствах) решения по этому вопросу, которые вы и я. Я понимаю, что они иметь доступ ко многим крупным реальным приложениям для сравнения/опробования эффектов оптимизации. У них также могут быть контакты в партнерских или клиентских организациях с такими же большими базами кода и озабоченностью по поводу эффективности... кого они могут попросить дать отзыв о том, работают ли оптимизации в раннем выпуске доступа должным образом.

1 - Это зависит от значения StringDeduplicationAgeThreshold JVM StringDeduplicationAgeThreshold.По умолчанию это значение 3, означающее, что (примерно) строка должна пережить 3 второстепенные коллекции или основную коллекцию, которую следует рассмотреть для устранения дублирования.Но в любом случае, если строка будет де-дуплицирована и вскоре после этого будет признана недоступной, накладные расходы на дедупликацию не будут погашены для этой строки.


Если вы спрашиваете, когда следует рассмотреть возможность включения дедупликации, я бы посоветовал попробовать и посмотреть, поможет ли это для каждого приложения. Но вам нужно провести некоторое тестирование на уровне приложений (которое требует усилий!), Чтобы быть уверенным, что устранение дублирования полезно...

Внимательное прочтение JEP 192 также поможет вам понять проблемы и принять решение о том, как они могут применяться к вашему Java-приложению.

Ответ 2

Я абсолютно понимаю, что это не отвечает на вопрос, просто хотел упомянуть, что jdk-9 вводит еще одну оптимизацию, которая по умолчанию называется:

-XX: + CompactStrings

где символы Latin1 занимают один байт вместо двух (через char). Из-за этого изменилось много внутренних методов String - они действуют одинаково для пользователя, но внутри они быстрее во многих случаях.

Также в случае строк для объединения двух строк вместе через знак плюса javac собирается генерировать другой байт-код.

Нет инструкции по байт-коду, которая объединяет две строки вместе, поэтому javac будет генерировать

StringBuilder # Append

в фоновом режиме. До jdk-9.

Теперь байт-код делегирует

StringConcatFactory # makeConcatWithConstants

или

StringConcatFactory # makeConcat

с помощью команды invokedynamic bytecode:

   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 

Как две конкатенированные строки являются решением Runtime сейчас. он может быть все еще StringBuilder или может быть конкатенацией массивов байтов и т.д. Все, что вы знаете, это может измениться, и вы получите самое быстрое решение.

ИЗМЕНИТЬ

Я только что отладил и увидел, что существует довольно много стратегий добавления этих строк:

    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
}

Значение по умолчанию:

MH_INLINE_SIZED_EXACT