Подтвердить что ты не робот

JAXB создает контекст и затраты маршаллеров

Вопрос немного теоретический, какова стоимость создания контекста JAXB, маршаллера и unmarshaller?

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

Итак, какова стоимость создания контекста JAXB и marshaller/unmarshaller? Можно ли создать контекст + маршаллер для каждой операции маршалинга или лучше избегать его?

4b9b3361

Ответ 1

Примечание. Я EclipseLink JAXB (MOXy) и член группы экспертов JAXB 2 ( JSR-222).

JAXBContext является потокобезопасным и его следует создавать только один раз и повторно использовать, чтобы избежать затрат на инициализацию метаданных несколько раз. Marshaller и Unmarshaller не являются потокобезопасными, но легкими для создания и могут быть созданы за операцию.

Ответ 2

В идеале вы должны иметь singleton JAXBContext и локальные экземпляры Marshaller и Unmarshaller.

JAXBContext экземпляры являются потокобезопасными, в то время как Marshaller и Unmarshaller экземпляры не потокобезопасны и никогда не должны делиться между потоками.

Ответ 3

Жаль, что это конкретно не описано в javadoc. Что я могу сказать, так это то, что Spring использует глобальный JAXBContext, совместно используемый потоками, тогда как он создает новый маршаллер для каждой операции маршаллинга с комментарием javadoc в коде, который говорит, что маршаллеры JAXB не обязательно являются поточно-ориентированными.

То же самое сказано на этой странице: https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety.

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

Ответ 4

Я решил эту проблему, используя безопасный общий поток JAXBContext и натолкнулся на локальный un/marschallers (теоретически, будет столько экземпляров un/marshaller, поскольку есть потоки, к которым они обращаются) с синхронизацией только при инициализации un/marshaller.

private final ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<Unmarshaller>() {
    protected synchronized Unmarshaller initialValue() {
        try {
            return jaxbContext.createUnmarshaller();
        } catch (JAXBException e) {
            throw new IllegalStateException("Unable to create unmarshaller");
        }
    }
};
private final ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<Marshaller>() {
    protected synchronized Marshaller initialValue() {
        try {
            return jaxbContext.createMarshaller();
        } catch (JAXBException e) {
            throw new IllegalStateException("Unable to create marshaller");
        }
    }
};

private final JAXBContext jaxbContext;

private MyClassConstructor(){
    try {
        jaxbContext = JAXBContext.newInstance(Entity.class);
    } catch (JAXBException e) {
        throw new IllegalStateException("Unable to initialize");
    }
}

Ответ 5

JAXB 2.2 (JSR-222) имеет это, например, в разделе "4.2 JAXBContext":

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

[..]

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

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

Ответ 6

Еще лучше! Основываясь на хорошем решении из поста выше, создайте контекст только один раз в конструкторе и сохраните его вместо класса.

Заменить строку:

  private Class clazz;

с этим:

  private JAXBContext jc;

И главный конструктор с этим:

  private Jaxb(Class clazz)
  {
     this.jc = JAXBContext.newInstance(clazz);
  }

поэтому в getMarshaller/getUnmarshaller вы можете удалить эту строку:

  JAXBContext jc = JAXBContext.newInstance(clazz);

Это улучшение делает в моем случае, что время обработки падает с 60 до 70 мс до 5 ~ 10 мс

Ответ 7

Обычно я решаю такие проблемы с помощью шаблона класса ThreadLocal. Учитывая тот факт, что для каждого класса вам нужен другой маршаллер, вы можете комбинировать его с шаблоном singleton -map.

Сэкономить 15 минут работы. Здесь следует моя реализация потокобезопасного Factory для Jaxb Marshallers и Unmarshallers.

Он позволяет вам обращаться к экземплярам следующим образом:

Marshaller m = Jaxb.get(SomeClass.class).getMarshaller();
Unmarshaller um = Jaxb.get(SomeClass.class).getUnmarshaller();

И код, который вам понадобится, - это немного класс Jaxb, который выглядит следующим образом:

public class Jaxb
{
  // singleton pattern: one instance per class.
  private static Map<Class,Jaxb> singletonMap = new HashMap<>();
  private Class clazz;

  // thread-local pattern: one marshaller/unmarshaller instance per thread
  private ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<>();
  private ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<>();

  // The static singleton getter needs to be thread-safe too, 
  // so this method is marked as synchronized.
  public static synchronized Jaxb get(Class clazz)
  {
    Jaxb jaxb =  singletonMap.get(clazz);
    if (jaxb == null)
    {
      jaxb = new Jaxb(clazz);
      singletonMap.put(clazz, jaxb);
    }
    return jaxb;
  }

  // the constructor needs to be private, 
  // because all instances need to be created with the get method.
  private Jaxb(Class clazz)
  {
     this.clazz = clazz;
  }

  /**
   * Gets/Creates a marshaller (thread-safe)
   * @throws JAXBException
   */
  public Marshaller getMarshaller() throws JAXBException
  {
    Marshaller m = marshallerThreadLocal.get();
    if (m == null)
    {
      JAXBContext jc = JAXBContext.newInstance(clazz);
      m = jc.createMarshaller();
      marshallerThreadLocal.set(m);
    }
    return m;
  }

  /**
   * Gets/Creates an unmarshaller (thread-safe)
   * @throws JAXBException
   */
  public Unmarshaller getUnmarshaller() throws JAXBException
  {
    Unmarshaller um = unmarshallerThreadLocal.get();
    if (um == null)
    {
      JAXBContext jc = JAXBContext.newInstance(clazz);
      um = jc.createUnmarshaller();
      unmarshallerThreadLocal.set(um);
    }
    return um;
  }
}