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


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

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

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

7 85

7 ответов:

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

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

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

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

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

то же самое сказано на этой странице:http://jaxb.java.net/guide/Performance_and_thread_safety.html.

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

Я решил эту проблему с помощью shared thread safe 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");
    }
}

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

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

[..]

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

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

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

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

  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мс

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

чтобы сэкономить 15 минут работы. Здесь следует моя реализация потокобезопасной фабрики для Маршаллеров Jaxb и 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;
  }
}