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

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

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

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

7 ответов


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

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


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

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


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

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

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


Я решил эту проблему с помощью shared thread safe JAXBContext и локального потока ООН/marschallers (так что теоретически будет столько же ООН/маршаллер экземпляры, поскольку есть потоки, которые обращались к ним) с синхронизацией только на ООН/маршаллер'ы.

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 требуется thread-safe, таким образом, несколько потоков в приложении, можете поделиться один экземпляр JAXBContext.

[..]

jaxbcontext класс разработан, чтобы быть неизменяемым и, таким образом, threadsafe. Учитывая объем динамической обработки, который потенциально может занять место при создании нового экземпляра 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~70ms к как раз 5~10ms


Я обычно решаю такие проблемы с помощью 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;
  }
}