Лучшая альтернатива для Строковой реализации в наилегчайшем весе в Java

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

Я посмотрел на Java Постоянный Пул и String.intern, но кажется, что он может вызвать некоторые проблемы PermGen.

Какова была бы лучшая альтернатива для реализации многопоточного пула всего приложения Строк в Java?

Править: Также посмотрите мой предыдущий, связанный вопрос: Как Java реализует шаблон в наилегчайшем весе для строки под капотом?

9
задан Community 23 May 2017 в 12:01
поделиться

4 ответа

Примечание: В этом ответе используются примеры, которые могут быть неактуальны в современных исполняемых библиотеках JVM. В частности, пример substring больше не является проблемой в OpenJDK/Oracle 7+.

Я знаю, что это противоречит тому, что вам часто говорят, но иногда явное создание новых экземпляров String может быть существенным способом уменьшить объем памяти.

Поскольку строки неизменяемы, некоторые методы используют этот факт и совместно используют массив символов подложки для экономии памяти. Однако иногда это может фактически увеличить память за счет предотвращения сборки мусора неиспользуемых частей этих массивов.

Например, предположим, вы разбираете идентификаторы сообщений в файле журнала, чтобы извлечь идентификаторы предупреждений. Ваш код выглядел бы примерно так:

//Format:
//ID: [WARNING|ERROR|DEBUG] Message...
String testLine = "5AB729: WARNING Some really really really long message";

Matcher matcher = Pattern.compile("([A-Z0-9]*): WARNING.*").matcher(testLine);
if ( matcher.matches() ) {
    String id = matcher.group(1);
        //...do something with id...
}

Но посмотрите на данные, которые на самом деле хранятся:

    //...
    String id = matcher.group(1);
    Field valueField = String.class.getDeclaredField("value");
    valueField.setAccessible(true);

    char[] data = ((char[])valueField.get(id));
    System.out.println("Actual data stored for string \"" + id + "\": " + Arrays.toString(data) );

Это вся строка test, потому что matcher просто оборачивает новый экземпляр String вокруг тех же символьных данных. Сравните результаты, когда вы замените String id = matcher.group(1); на String id = new String(matcher.group(1));.

8
ответ дан 4 December 2019 в 15:12
поделиться

Это уже сделано на уровне JVM. Вам нужно только убедиться, что вы не создаете new String каждый раз явно или неявно.

Т.е. не делайте:

String s1 = new String("foo");
String s2 = new String("foo");

Это создаст два экземпляра в куче. Скорее сделайте так:

String s1 = "foo";
String s2 = "foo";

Это создаст один экземпляр в куче, и оба будут ссылаться на одно и то же (в качестве доказательства, s1 == s2 вернет здесь true ).

Также не используйте + = для объединения строк (в цикле):

String s = "";
for (/* some loop condition */) {
    s += "new";
}

+ = неявно создает новую строку в куча каждый раз. Скорее сделайте это

StringBuilder sb = new StringBuilder();
for (/* some loop condition */) {
    sb.append("new");
}
String s = sb.toString();

Если можете, лучше используйте StringBuilder или его синхронизированный брат StringBuffer вместо String для «интенсивной обработки строк». Он предлагает полезные методы именно для этих целей, такие как append () , insert () , delete () и т. Д. Также см. его javadoc .

3
ответ дан 4 December 2019 в 15:12
поделиться

Эффективная упаковка строк в памяти! Однажды я написал класс Set с эффективным использованием гиперпамяти, в котором строки хранились в виде дерева. Если лист был достигнут путем обхода букв, запись содержалась в наборе. Быстро работать и идеально подходит для хранения большого словаря.

И не забывайте, что строки часто являются самой большой частью памяти почти в каждом профилированном мной приложении, поэтому не обращайте на них внимания, если они вам нужны.

Иллюстрация:

У вас есть 3 струны: пиво, бобы и кровь. Вы можете создать такую ​​древовидную структуру:

B
+-e
  +-er
  +-ans
+-lood

Очень эффективно, например, список названий улиц, это, очевидно, наиболее разумно с фиксированным словарем, потому что вставка не может быть выполнена эффективно. Фактически структура должна быть создана один раз, затем сериализована и после этого просто загружена.

1
ответ дан 4 December 2019 в 15:12
поделиться

Сначала решите, насколько пострадает ваше приложение и разработчики, если вы избавитесь от части парсинга. Более быстрое приложение не принесет вам никакой пользы, если в процессе удвоится текучесть кадров! Думаю, исходя из вашего вопроса, можно предположить, что вы уже прошли этот тест.

Во-вторых, если вы не можете отказаться от создания объекта, то вашей следующей целью должно стать обеспечение того, чтобы он не пережил Eden collection. И parse-lookup может решить эту проблему. Однако кэш, "реализованный должным образом" (я не согласен с этой базовой предпосылкой, но не буду утомлять вас сопутствующими разглагольствованиями), обычно приводит к спорам о потоках. Вы заменяете один вид давления на память другим.

Существует разновидность идиомы разбора-поиска, которая меньше страдает от побочного ущерба, который обычно возникает при полном кэшировании, и это простая предварительно вычисленная таблица поиска (см. также "мемоизация"). Шаблон, который вы обычно видите для этого, - Type Safe Enumeration (TSE). С помощью TSE вы разбираете строку, передаете ее в TSE для получения соответствующего перечислимого типа, а затем выбрасываете строку.

Текст, который вы обрабатываете, имеет свободную форму, или ввод должен соответствовать жесткой спецификации? Если большая часть вашего текста сводится к фиксированному набору возможных значений, то TSE может помочь вам здесь и послужить большему мастеру: Добавление контекста/семантики к вашей информации в момент создания, а не в момент использования.

0
ответ дан 4 December 2019 в 15:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: