Отключение интернирования строк в Jaxb2 ускоряет демаршаллинг из потока Fastinfoset. Почему так сложно отключить?

Я измерил временные затраты на демонтаж XML для объектов, использующих Jaxb2, с использованием большой (1,7 МБ) полезной нагрузки XML с несколько длинными (48 символов) именами тегов. Через JProfiler, работающий в режиме выборки, я заметил, что работа по интернированию строк занимает большую часть затраченного времени.

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

Метод, который я использовал для подавления интернирующего поведения Jaxb, заключался в том, чтобы установить в моем Fastinfoset "StAXDocumentParser" (который реализует XMLStreamReader) свойства "org.codehaus.stax2.internNames" и "org.codehaus.stax2.internNsUris". Мне не на 100% ясно, почему вы должны установить для них значение "true", чтобы Jaxb не интернировал строки, но именно так оно и работает.

Эти тесты на основе JUnit- это то, что я использовал для вывода о том, что отключение поведения интернирования строк в Jaxb сильно влияет на производительность:

https://github.com/gjd6640/fastinfoset-performance-evaluation

Итак, мой вопрос состоит из нескольких частей:

1) Я неправильно понимаю что-то важное и не должен пытаться отключить поведение Jaxb для интернирования строк?

2) Есть ли лучший способ направить Jaxb, чтобы не интернировать строки? Класс "StAXManager" не позволяет вам устанавливать эти Woodstox-ориентированные свойства. В этом тесте я расширил StAXManager, как показано ниже, для решения проблемы. Это хак, который я предпочел бы не использовать в производстве. Я подозреваю, что идея заключается в том, что когда Jaxb выполняет демаршаллинг из потока Woodstox, он проверяет, выполняет ли Woodstox уже интернирование, и когда "да" Jaxb реагирует отключением этого шага процесса. Я обманываю, используя эту логику в библиотеке Jaxb, поэтому хотел бы найти лучший способ сделать это.

package com.sun.xml.fastinfoset.stax;
public class JaxbStringInternSuppressionStaxManager extends StAXManager {
    public JaxbStringInternSuppressionStaxManager() {
        // Add to the allowable list of feature names so that the user may set these "StAXInputFactory" properties
        super.features.put("org.codehaus.stax2.internNames", null);
        super.features.put("org.codehaus.stax2.internNsUris", null);
    }
}

Обновить:

Как обычно, "хорошо поставленный вопрос - наполовину ответ". Я только заметил, что при составлении этого вопроса "com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector" проверяет, является ли класс "com.sun.xml.Internal.fastinfoset.stax.StAXDocumentParser" присваивается из используемого вами XMLStreamReader и в этом случае не включает интернирование строк. В моем случае мой потоковый объект является "com.sun.xml.fastinfoset.stax.StAXDocumentParser", поэтому интернирование не отключается. Теперь возникает вопрос: "Почему он делает это только для внутреннего вкуса библиотеки Fastinfoset?" Может быть, я найду ответ, внимательно прочитав этот пост.

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

2 ответа

Я бы не стал доверять профилировщику или тесту, не измеряя реальный вариант использования с интернированием и без него, так что будьте немного скептиками. Тем не менее, есть некоторые проблемы со стажером. В частности, он использует пул фиксированного размера, поэтому, когда пул заполнен, потенциальная постоянная производительность для поиска в хэшах снижается до поиска в связанных списках. См. http://java-performance.info/string-intern-in-java-6-7-8/ для более подробного обсуждения.

Короче говоря, вы можете попытаться изменить размер пула с -XX:StringTableSize=n (где n в идеале должно быть простым) и посмотрим, что произойдет.

использование -XX:+PrintStringTableStatistics чтобы увидеть, как пул использовался при завершении программы, и попробуйте разные размеры.

РЕДАКТИРОВАТЬ: это была попытка ответить "есть ли лучший путь" (т.е. сделать стажера быстрее). Я оставлю другой вопрос кому-то более квалифицированному.

Вариант решения 1. Простой подход, при котором все приложение переключается на другую реализацию jaxb.

Вставьте jaxb-impl, чтобы использовать версию Jaxb, которая работает лучше с этой библиотекой Fastinfoset:

<!-- Both of these libs must be here in order to get performant behavior out of Jaxb by default.
-->
<dependency>
        <groupId>com.sun.xml.fastinfoset</groupId>
        <artifactId>FastInfoset</artifactId>
        <version>1.2.13</version>
        <scope>compile</scope>
</dependency>
<dependency> <!-- This artifactId also exists under javax.xml.bind but it appears that nobody uses that one... -->
    <groupId>javax.xml</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.1</version>
    <scope>runtime</scope>
</dependency>
<!-- End: Both of these libs... -->

Это будет иметь побочный эффект обновления версии jaxb, используемой остальной частью вашего кода. В некоторых ситуациях это может быть нежелательно. Например, если вы создаете разделяемую библиотеку, которую необходимо использовать в различных приложениях, было бы грубо пойти и изменить эту функцию, когда они загружают ваш разделяемый компонент.

Вариант решения 2. Используйте реализацию jaxb JVM и хак производительности, чтобы заставить его поверить, что строки уже интернированы (более сложный для реализации)

  • Используйте "maven-shade-plugin", чтобы закрасить и упаковать классы библиотеки Fastinfoset. Результат должен быть логическим компонентом maven. Это необязательно и предназначено для того, чтобы у людей, использующих ваш компонент кодека Fastinfoset, не возникало коллизий пути к классам из-за транзитивных зависимостей, извлекаемых вашей библиотекой кодеков.
  • Создайте библиотеку my-fastinfoset-codec, которая предоставляет простой API для кодирования и декодирования полезных нагрузок Fastinfoset (рассмотрите возможность использования InputStreams и OutputStreams для аргументов и XMLStreamReader для возвращаемого типа декодера). Добавьте зависимость от вашей переупакованной библиотеки Fastinfoset. Обратите внимание, что если вы используете Eclipse, он плохо работает с затененными библиотеками, когда "разрешение рабочего пространства" в m2e включено, поэтому отключите его для своего проекта кодека.
  • Добавьте в my-fastinfoset-codec класс, который расширяет пакет StAXManager из перепакованной библиотеки Fastinfoset. Этот класс должен облегчать установку свойств, которые сообщают jaxb, что предоставленный ему XMLStreamReader уже интернировал строки имен NS и тегов. Пример ниже:
    пакет myrepackagedfastinfosetclassespackageprefix.shaded.com.sun.xml.fastinfoset.stax;
    import myrepackagedfastinfosetclassespackageprefix.shaded.com.sun.xml.fastinfoset.stax.StAXManager;
    открытый класс JaxbStringInternSuppressionStaxManager extends StAXManager {
        public JaxbStringInternSuppressionStaxManager() {
            // Добавить в допустимый список имен объектов, чтобы пользователь мог установить эти свойства "StAXInputFactory"
            super.features.put("org.codehaus.stax2.internNames", null);
            super.features.put("org.codehaus.stax2.internNsUris", null);
        }

        /**
         * Это оптимизация. Библиотеки FastInfoset уже содержат строки и реализацию jaxb JVM по умолчанию 
         * излишне повторяет эту работу. Это верно по крайней мере для 64-битной версии jdk1.8.0_121.
         * 
         * Способ работы этого обходного пути заключается в использовании оптимизации Jaxb для анализатора Woodstox. Когда мы установим
         * эти свойства говорят jaxb, что Woodstox уже интернировал строки, что заставляет его отключать
         * интернирование строк.
         * 
         * Мы исследовали более чистый вариант добавления артефакта Maven "javax.xml:jaxb-impl" в качестве зависимости вместо использования
         * Jax-библиотека JVM. Эта внешняя библиотека jaxb при использовании с библиотекой FastInfoset работает значительно лучше
         * чем JVM, но не на 100% быстрее, чем JVM с отключенным интернированием. Основная причина, по которой мы прекратили исследовать это решение
         * в том, что когда вы перепаковываете (через maven-shade-plugin) библиотеки jaxb, они больше не работают с нашей стандартной привязкой jaxb
         * компоненты maven из-за операторов типа "if ( instanceof my_repackaging_project.shaded.XMLElement)"
         * используется в процессе отображения данных.
         */
        public JaxbStringInternSuppressionStaxManager enableTrickToStopJaxbFromInterningStrings() {
            super.setProperty("org.codehaus.stax2.internNames", true);
            super.setProperty("org.codehaus.stax2.internNsUris", true);
            верни это;
        }
    }

Вариант решения 3. Достаточно людей, имеющих контракт на поддержку JVM с Oracle, собирают билеты, требующие какой-либо внешней поддержки fastinfoset.

Я ожидаю, что для Oracle будет довольно просто научить Jax-реализацию, предоставляемую JVM, определять из заданного XMLStreamReader, что эта реализация Fastinfoset сконфигурирована для интернирования строк.

Возможность решения, которая не удалась: упакуйте две банки из решения 1 выше

Можно использовать "maven-shade-plugin" или аналогичный для создания новых jar-файлов с именами пакетов с префиксом. Это сработало с этими библиотеками после некоторой возни. Однако в результате я пришел к тому, что переупакованные библиотеки jaxb теперь хотели, чтобы объекты OXM, созданные jaxb-RI, имели аннотации от нового заштрихованного имени пакета. Мои были построены стандартным способом, поэтому мое переупакованное решение не отображало данные на мои объекты. Я не хочу диктовать, что наши библиотеки связывания OXM используют переупакованную библиотеку jaxb, и мне не понравился этот подход, чтобы более тщательно изучить способы переупаковки, чтобы не изменять пакет, используемый для этих аннотаций.

Вариант решения, который я не исследовал:

Используйте классы fastinfoset JVM, имеющие ".internal". в их названиях пакетов. Они, вероятно, будут работать хорошо с реализацией jaxb, которая поставляется с JVM, но я отказываюсь подвергать "будущее меня" расходам на поддержку, связанным с использованием внутреннего apis.

Другие вопросы по тегам