JAXB создает контекст и затраты маршаллеров
Вопрос немного теоретический, какова стоимость создания контекста JAXB, маршаллера и unmarshaller?
Я обнаружил, что мой код может выиграть от сохранения одного и того же контекста JAXB и, возможно, одного и того же маршаллера для всех операций маршалинга, а не для создания контекста и маршаллера при каждом маршалинге.
Итак, какова стоимость создания контекста JAXB и marshaller/unmarshaller? Можно ли создать контекст + маршаллер для каждой операции маршалинга или лучше избегать его?
Ответы
Ответ 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;
}
}