Есть ли эффективная замена памяти java.lang.String? - PullRequest
36 голосов
/ 23 октября 2008

После прочтения этой старой статьи измерения потребления памяти несколькими типами объектов я был поражен, увидев, сколько памяти String s используется в Java:

length: 0, {class java.lang.String} size = 40 bytes
length: 7, {class java.lang.String} size = 56 bytes

Хотя в статье есть несколько советов по минимизации этого, я не нашел их полностью удовлетворительными. Кажется, расточительно использовать char[] для хранения данных. Очевидным улучшением для большинства западных языков стало бы использование byte[] и кодировки, такой как UTF-8, поскольку вам требуется только один байт для хранения наиболее часто встречающихся символов, а не два байта.

Конечно, можно использовать String.getBytes("UTF-8") и new String(bytes, "UTF-8"). Даже накладные расходы самого экземпляра String исчезли бы. Но тогда вы теряете очень удобные методы, такие как equals(), hashCode(), length(), ...

Насколько я могу судить, у Sun есть патент на byte[] представление строк.

Рамки для эффективного представления строковых объектов в средах программирования Java
... Методы могут быть реализованы для создания строковых объектов Java в виде массивов однобайтовых символов, когда это уместно ...

Но мне не удалось найти API для этого патента.

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

Кто-нибудь знает такой API? Или есть другой способ сохранить небольшой объем памяти для строк, даже за счет производительности процессора или более уродливого API?

Пожалуйста, не повторяйте предложения из вышеприведенной статьи:

  • собственный вариант String.intern() (возможно с SoftReferences)
  • хранение одного char[] и использование текущей реализации String.subString(.), чтобы избежать копирования данных (неприятно)

Обновление

Я запустил код из статьи о текущей JVM от Sun (1.6.0_10). Он дал те же результаты, что и в 2002 году.

Ответы [ 15 ]

1 голос
/ 24 октября 2008

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

Если вы собираетесь использовать ОО-программирование, то это стоимость наличия понятного, пригодного для использования и поддерживаемого кода.

Для ответа, помимо очевидного (который состоит в том, что, если использование памяти так важно, вы, вероятно, должны использовать C), вы можете реализовать свои собственные строки с внутренним представлением в байтовых массивах BCD.

На самом деле это звучит забавно, я мог бы сделать это только для удовольствия:)

Массив Java занимает 2 байта на элемент. Цифра в кодировке BCD занимает 6 бит на букву IIRC, что делает ваши строки значительно меньше. Время конверсии будет немного, но на самом деле не так уж и плохо. Действительно большая проблема в том, что вам нужно преобразовать строку, чтобы что-то с ней сделать.

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

Наконец записка. Я полностью против развертывания чего-либо подобного, если у вас нет 3 вещей:

  • Реализация сделана наиболее читабельным способом
  • Результаты испытаний и требования, показывающие, как эта реализация не соответствует требованиям
  • Результаты тестирования того, как "улучшенная" реализация соответствует требованиям.

Без всех трех я бы выбил любое оптимизированное решение, которое разработчик представил мне.

0 голосов
/ 26 октября 2008

Помните, что существует много типов сжатия. Использование кодирования Хаффмана является хорошим подходом общего назначения, но оно относительно интенсивно использует процессор. Для реализации B + Tree, над которой я работал несколько лет назад, мы знали, что ключи, вероятно, будут иметь общие начальные символы, поэтому мы реализовали алгоритм сжатия ведущих символов для каждой страницы в B + Tree. Код был простым, очень, очень быстрым и привел к использованию памяти на 1/3 от того, с чего мы начали. В нашем случае настоящей причиной для этого было сэкономить место на диске и сократить время, затрачиваемое на диск -> передача ОЗУ (и эта экономия 1/3 имела огромное значение для эффективной производительности диска).

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

Попытка оптимизировать несколько байтов здесь и там внутри объекта String может не стоить того для сравнения.

0 голосов
/ 24 октября 2008

Вы сказали, что не следует повторять предложение статьи о развертывании вашей собственной схемы интернирования, но что не так с самим String.intern? Статья содержит следующее одноразовое замечание:

Существует множество причин избегать метода String.intern (). Во-первых, немногие современные JVM могут обрабатывать большие объемы данных.

Но даже если бы данные об использовании памяти за 2002 год все еще сохранялись шесть лет спустя, я был бы удивлен, если бы не было достигнуто никакого прогресса в том, сколько данных JVM может проходить.

Это не чисто риторический вопрос - мне интересно знать, есть ли веские причины избегать этого. Неэффективно ли оно реализовано для многопоточного использования? Заполняет ли он какую-то особую JVM-специфическую область кучи? У вас действительно есть сотни мегабайт уникальных строк (так что интернирование было бы бесполезно в любом случае)?

0 голосов
/ 24 октября 2008

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

0 голосов
/ 23 октября 2008

Из любопытства, действительно ли сэкономлено несколько байтов?

Обычно я рекомендую исключать строки из соображений производительности в пользу StringBuffer (помните, строки являются неизменяемыми).

Вы серьезно исчерпываете кучу ссылок на строки?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...