JAXB creación de contexto y coste de los marshallers


La pregunta es un poco teórica, ¿cuál es el costo de crear contexto JAXB, marshaller y unmarshaller?

He encontrado que mi código podría beneficiarse de mantener el mismo contexto JAXB y posiblemente el mismo marshaller para todas las operaciones de marshaling en lugar de crear contexto y marshaller en cada marshaling.

Entonces, ¿cuál es el costo de crear contexto JAXB y marshaller/unmarshaller? ¿Está bien crear contexto + marshaller para cada operación de marshaling o es mejor para evitarlo?

Author: Vladimir, 2011-09-13

7 answers

Nota: yo soy el EclipseLink JAXB (MOXy) el plomo y un miembro de la JAXB 2 (JSR-222) grupo de expertos.

JAXBContext es seguro para subprocesos y solo debe crearse una vez y reutilizarse para evitar el costo de inicializar los metadatos varias veces. Marshaller y Unmarshaller no son seguros para subprocesos, pero son livianos para crear y podrían crearse por operación.

 187
Author: Blaise Doughan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-13 10:57:03

Idealmente, debería tener un singleton JAXBContext e instancias locales de Marshaller y Unmarshaller.

JAXBContext las instancias son seguras para subprocesos, mientras que Marshaller y Unmarshaller las instancias son no seguras para subprocesos y nunca deben compartirse entre subprocesos.

 32
Author: Sahil Muthoo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-01 08:53:37

Es una pena que esto no esté específicamente descrito en el javadoc. Lo que puedo decir es que Spring usa un JAXBContext global, compartido entre subprocesos, mientras que crea un nuevo marshaller para cada operación de marshalling, con un comentario javadoc en el código diciendo que los marshallers JAXB no son necesariamente seguros para subprocesos.

Lo mismo se dice en esta página: http://jaxb.java.net/guide/Performance_and_thread_safety.html.

Supongo que crear un JAXBContext es una operación costosa, porque implica escanear clases y paquetes para anotaciones. Pero medirlo es la mejor manera de saberlo.

 11
Author: JB Nizet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-13 10:57:36

Resolví este problema usando shared thread safe JAXBContext y thread local un / marschallers (así que teóricamente, habrá tantas instancias un/marshaller como subprocesos que accedan a ellas) con sincronización solo en la inicialización de 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
Author: peeeto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-18 16:31:05

JAXB 2.2 (JSR-222) tiene esto que decir, en la sección "4.2 JAXBContext":

Para evitar la sobrecarga involucrada en la creación de un JAXBContext instancia, a Se anima a la aplicación JAXB a reutilizar una instancia JAXBContext. Un la implementación de la clase abstracta JAXBContext debe ser thread-safe , por lo tanto, varios subprocesos en una aplicación pueden compartir misma instancia de JAXBContext.

[..]

La clase JAXBContext está diseñada para ser inmutable y por lo tanto threadsafe. Dada la cantidad de procesamiento dinámico que potencialmente podría tomar lugar al crear una nueva instancia de JAXBContxt, se recomienda que una instancia de JAXBContext se comparta entre subprocesos y se reutilice como tanto como sea posible para mejorar el rendimiento de la aplicación.

Desafortunadamente, la especificación no hace ninguna afirmación con respecto a la seguridad del hilo Unmarshaller y Marshaller. Así que es mejor asumir que no lo son.

 2
Author: Martin Andersson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-20 15:15:24

Aún mejor!! Basado en la buena solución del post anterior, crea el contexto solo una vez en el constructor, y guárdalo en lugar de la clase.

Reemplace la línea:

  private Class clazz;

Con este:

  private JAXBContext jc;

Y el constructor principal con este:

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

Así que en getMarshaller / getUnmarshaller puedes eliminar esta línea:

  JAXBContext jc = JAXBContext.newInstance(clazz);

Esta mejora hace, en mi caso, que los tiempos de procesamiento desciendan de 60~70ms a solo 5~10ms

 1
Author: tbarderas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-29 08:51:49

Normalmente resuelvo problemas como este con un patrón de clase ThreadLocal. Dado el hecho de que necesita un marshaller diferente para cada Clase, puede combinarlo con un patrón singleton-map.

Para ahorrarte 15 minutos de trabajo. Aquí sigue mi implementación de una fábrica segura para hilos para Marshallers y Unmarshallers de Jaxb.

Le permite acceder a las instancias de la siguiente manera ...

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

Y el código que necesitará es una pequeña clase Jaxb que se ve de la siguiente manera:

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;
  }
}
 0
Author: bvdb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-08 16:12:33