Использование дедупликации строк | Кодементор

UseStringDeduplication — плюсы и минусы
Позвольте мне начать эту статью с интересной статистики (основанной на исследовании, проведенном командой разработчиков JDK):

  • 25% памяти приложений Java заполнено строками.
  • 13,5% — повторяющиеся строки в Java-приложениях.
  • Средняя длина строки составляет 45 символов.

Да, вы правы, 13,5% памяти тратится впустую из-за повторяющихся строк. 13,5% — это среднее количество повторяющихся строк в Java-приложении. Чтобы выяснить, сколько памяти ваше приложение тратит впустую из-за повторяющихся строк, вы можете использовать такие инструменты, как HeapHero, которые могут сообщать, сколько памяти тратится впустую из-за повторяющихся строк и других неэффективных приемов программирования.

Что такое повторяющиеся строки?
Во-первых, давайте разберемся, что означает повторяющаяся строка. Посмотрите на приведенный ниже фрагмент кода:

String string1 = new String("Hello World");
String string2 = new String("Hello World");

В приведенном выше коде есть два строковых объекта string1 и string2, они имеют одинаковое содержимое, т.е. «Hello World», но хранятся в двух разных объектах. Когда вы делаете string1.equals(string2), он возвращает «true», но «string1 == string2» возвращает «false». Это то, что мы называем повторяющимися строками.

Почему так много повторяющихся строк?
Есть несколько причин, по которым в приложении появляется много повторяющихся строк. В этом разделе мы рассмотрим два наиболее распространенных шаблона:

  1. Разработчики создают новые строковые объекты для каждого запроса вместо ссылки/повторного использования «публичного статического конечного строкового литерала». Приведенный выше пример может быть оптимально написан с использованием шаблона строкового литерала:
public static final String HELLO_WORLD = "Hello World";

String string1 = HELLO_WORLD;
String string2 = HELLO_WORLD;

2. Предположим, вы создаете приложение для банковских операций/электронной коммерции. Вы храните валюту (т.е. «USD», «EUR», «INR», ….) для каждой записи транзакции в базе данных. Скажем, теперь клиент входит в ваше приложение, и он просматривает страницу истории транзакций. Теперь ваше приложение будет читать все транзакции, относящиеся к этому клиенту, из базы данных. Предположим, что этот клиент живет в США (тогда большинство, если не все его транзакции будут в долларах США). Поскольку каждая запись транзакции имеет валюту, ваше приложение в конечном итоге создаст строковый объект «USD» для каждой записи транзакции, считанной из базы данных. Если у этого клиента есть тысячи транзакций, вы в конечном итоге создадите в памяти тысячи повторяющихся строковых объектов «USD» только для этого одного клиента.

Точно так же ваше приложение может считывать несколько столбцов (имя клиента, адрес, штат, страна, номер счета, идентификаторы и т. д.) из баз данных несколько раз. Среди них могут быть дубликаты. Ваше приложение читает и записывает XML/JSON с внешними приложениями, оно манипулирует множеством строк. Все эти операции могут/будут создавать повторяющиеся строки.

Эта проблема давно известна команде JDK с момента ее возникновения (середина 1990-х годов), поэтому до сих пор было найдено несколько решений. Последнее дополнение к этому списку решений — «-XX:+UseStringDeduplication».

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

Означает ли это, что если вы просто передадите аргумент JVM ‘-XX:+UseStringDeduplication’, вы сможете сразу сэкономить 13,5% памяти? Звучит очень просто, верно? Мы хотим, чтобы это было так просто. Но у этого решения «-XX:+UseStringDeduplication» есть некоторые недостатки. Давайте обсудим их.

(1). Работает только с алгоритмом G1 GC
Существует несколько алгоритмов сборки мусора (Serial, Parallel, CMS, G1,…). ‘-XX:+UseStringDeduplication’ работает, только если вы используете алгоритм G1 GC. Таким образом, если вы используете какой-либо другой алгоритм сборки мусора, вам нужно переключиться на алгоритм G1 GC, чтобы использовать «-XX:+UseStringDeduplication».

(2). Работает только на долгоживущих объектах
‘-XX:+UseStringDeduplication’ устраняет повторяющиеся строки, которые существуют в течение более длительного периода времени. Они не устраняют повторяющиеся строки среди недолговечных строковых объектов. Если объекты недолговечны, они скоро умрут, то какой смысл тратить ресурсы на устранение повторяющихся строк среди них. Вот кейс из реальной жизни проведено в крупном веб-приложении Java, которое не показало никакого освобождения памяти при использовании «-XX: + UseStringDeduplication». Однако ‘-XX:+UseStringDeduplication’ может иметь значение, если ваше приложение имеет много кешей (поскольку объекты кеша обычно являются долгоживущими объектами).

(3). -XX:StringDeduplicationAgeThreshold
По умолчанию строки становятся доступными для дедупликации, если они выдержали 3 запуска GC. Его можно изменить, передав ‘-XX:StringDeduplicationAgeThreshold’.

Example:
-XX:StringDeduplicationAgeThreshold=6

(4). Влияние на время паузы GC
Поскольку дедупликация строк выполняется во время сборки мусора, это может повлиять на время приостановки сборки мусора. Однако предполагается, что достаточно высокий уровень успешности дедупликации сбалансирует большую часть или все это влияние, поскольку дедупликация может уменьшить объем работы, необходимой на других этапах приостановки сборки мусора (например, уменьшить количество объектов для эвакуации), а также уменьшить частота GC (из-за уменьшения нагрузки на кучу). Чтобы проанализировать влияние времени паузы GC, вы можете рассмотреть возможность использования таких инструментов, как GCeasy

(5). Только базовый символ[ ] заменяется
Класс java.lang.String имеет два поля:

private final char[] value
private int hash

‘-XX:+UseStringDeduplication’ не устраняет дублирующийся строковый объект. Он заменяет только базовый char[ ]. Дедупликация объекта String концептуально представляет собой просто переназначение поля значения, т. е. aString.value = otherString.value.

Каждый строковый объект занимает не менее 24 байт (точный размер строкового объекта зависит от конфигурации JVM, но минимум 24 байта). Таким образом, эта функция экономит меньше памяти, если есть много коротких повторяющихся строк.

(6). Java 8 обновление 20
Функция «-XX:+UseStringDeduplication» поддерживается только в версии Java 8 с обновлением 20. Таким образом, если вы работаете в более старых версиях Java, вы не сможете использовать эту функцию.

(7). -XX:+Статистика дедупликации PrintString
Если вы хотите просмотреть статистику дедупликации строк, например, сколько времени потребовалось для выполнения, сколько повторяющихся строк было удалено, сколько вы сэкономили, вы можете передать аргумент JVM ‘-XX:+PrintStringDeduplicationStatistics’. В консоли ошибок будет напечатана статистика.

Вывод:
Если ваше приложение использует сборщик мусора G1 и работает на версии выше Java 8, обновление 20, вы можете рассмотреть возможность включения «-XX:+UseStringDeduplication». Вы можете получить плодотворные результаты, особенно если среди долгоживущих объектов много повторяющихся строк. Тем не менее, проведите тщательное тестирование перед включением этого аргумента в производственной среде.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *