Избегайте дублирования строк в Java - PullRequest
4 голосов
/ 22 февраля 2011

Я хочу задать вопрос об избежании дублирования строк в Java.

Контекст - это XML с тегами и атрибутами, подобными этому:

<product id="PROD" name="My Product"...></product>

В JibX этот XML маршалируется / демаршализируется в таком классе:

public class Product{
private String id;
private String name;
// constructor, getters, setters, methods  and so on
}

Программа является длительной пакетной обработкой, поэтому объекты Product создаются, используются, копируются и т. Д.

Что ж, вопрос : Когда я проанализировал выполнение с помощью программного обеспечения, такого как Анализатор памяти Eclipse (MAT) , я обнаружил несколько дублированных строк. Например, в атрибуте id значение PROD дублируется примерно в 2000 экземплярах и т. Д.

Как мне избежать этой ситуации? Другие атрибуты в классе Product могут изменять свое значение при выполнении, но атрибуты типа id , name ... меняются не так часто.

Я кое-что прочитал о String.intern () метод, но я еще не использовал, и я не уверен, что это решение для этого. Могу ли я определить наиболее частые значения в этих атрибутах, такие как static final константы в классе?

Надеюсь, я бы правильно сформулировал свой вопрос. Любая помощь или совет очень ценится. Заранее спасибо.

Ответы [ 5 ]

12 голосов
/ 22 февраля 2011

интернирование было бы правильным решением, если у вас действительно есть проблема. Java хранит строковые литералы и множество других строк во внутреннем пуле, и когда новая строка собирается быть созданной, JVM сначала проверяет, находится ли строка уже в пуле. Если да, он не создаст новый экземпляр, но передаст ссылку на interned String объект.

Есть два способа контролировать это поведение:

String interned = String.intern(aString); // returns a reference to an interned String
String notInterned = new String(aString); // creates a new String instance (guaranteed)

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


intern имеет глобальный эффект. Внутренняя строка сразу доступна «для любого объекта» (это представление на самом деле не имеет смысла, но может помочь понять его).

Итак, допустим, у нас есть строка в классе Foo, метод foolish:

String s = "ABCD";

Строковые литералы интернируются немедленно. JVM проверяет, находится ли «ABCD» в пуле, если нет, «ABCD» хранится в пуле. JVM назначает ссылку на интернированную строку для s.

Теперь, может быть, в другом классе Bar, в методе barbar:

String t = "AB"+"CD";

Затем JVM интернирует «AB» и «CD», как описано выше, создает сцепленную строку, смотрит, если она уже есть, Эй, да, это так, и присваивает ссылку на интернированную строку «ABCD» для t.


Звонок "PROD".intern() может работать или не работать. Да, это будет интерна String "PROD". Но есть вероятность, что Jibx действительно создает новые строки для значений атрибутов с

String value = new String(getAttributeValue(attribute));

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

И, к другому вопросу в вашей команде: это происходит только во время выполнения. Компиляция просто создает файлы классов, пул строк - это структура данных в куче объектов, которая используется JVM, которая выполняет приложение.

6 голосов
/ 22 февраля 2011

Хотя String.intern() может решить эту проблему, сократив каждое значение до одного уникального экземпляра String, это создаст еще одну проблему: каждый intern() -ed String может существовать долго время в JVM. Если идентификаторы сильно различаются (то есть они не являются частью ограниченного набора, но могут иметь любое значение), то это может иметь серьезные негативные последствия в долгосрочной перспективе.

Edit : Раньше я утверждал, что строки intern() -ed никогда не могут быть GCed, но @nanda доказала, что я не прав с этой статьей JavaWorld . Хотя это несколько уменьшает проблему, вызванную intern(), она все еще не полностью удалена: пул, предоставляемый intern(), не может контролироваться и может привести к неожиданным результатам в отношении сбора мусора).

К счастью Guava предоставляет решение в виде интерфейса Interner и его вспомогательного класса Interners: использование Interners.newStrongInterner() вы можете создать объект, который может действовать как «пул» уникальных String объектов почти так же, как String.intern(), за исключением того, что пул привязан к этому экземпляру, и если вы отбрасываете пул, тогда содержимое также может быть использовано для сбора мусора.

1 голос
/ 22 февраля 2011

Да, интернирование - это правильное решение, и вы выполнили домашнюю работу (то есть с помощью профилировщика выясните, что это проблема).

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

Некоторые вспомогательные статьи:

  1. Мой блог: http://blog.firdau.si/2009/01/06/java-tips-memory-optimization-for-string/
  2. Собрал ли мусор стажер ?: http://www.javaworld.com/javaworld/javaqa/2003-12/01-qa-1212-intern.html
  3. Разорение «Мифы об уничтожении струн. Intern ()»: http://kohlerm.blogspot.com/2009/01/is-javalangstringintern-really-evil.html
0 голосов
/ 03 мая 2013

Как всем известно, объекты String можно создавать двумя способами: с помощью литералов и оператора new.

Если вы используете литерал типа String test = "Sample";, тогда он будет кэшироваться в пуле объектов String. Таким образом, интернирование здесь не требуется, так как по умолчанию строковый объект будет кэшироваться.

Но если вы создаете строковый объект, такой как String test = new String ("Sample"); тогда этот строковый объект не будет добавлен в пул строк. Поэтому здесь нам нужно использовать String test = new String("Sample").intern(); для принудительного помещения строкового объекта в строковый кеш.

Так что всегда лучше использовать строковые литералы, чем оператор new.

Так что в вашем случае private static final String id = "PROD"; правильное решение.

0 голосов
/ 22 февраля 2011

Альтернативное решение:

Вы можете попытаться определить ограничение <xs:enumeration/> для вашего атрибута @id (если ваша модель домена допускает такую ​​вещь).Если JibX столь же интеллектуален, как JAXB или другие стандарты сопоставления XML-Java, то это может быть сопоставлено как Java enum с постоянными литералами, которые можно многократно использовать.

Я быпопробуйте это для значения ID, так как оно для меня выглядит как перечисление ...

...