2011-09-13 19 views
73

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

He descubierto que mi código podría beneficiarse de mantener el mismo contexto JAXB y posiblemente el mismo marcador para todas las operaciones de cálculo de referencias en lugar de crear contexto y marcador en cada cálculo.

¿Cuál es el costo de crear JAXB context y marshaller/unmarshaller? ¿Está bien crear context + marshaller para cada operación de cálculo de referencias o es mejor evitarlo?

Respuesta

154

Nota: Soy el EclipseLink JAXB (MOXy) de plomo y un miembro del grupo 2 (JSR-222) JAXB experto.

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

+5

gran respuesta. Puedo confiar ahora en tu experiencia como líder en JAXB. – Vladimir

+6

Confío en usted, pero ¿se encuentra esto en algún lugar de la documentación? – Hurda

+2

Está documentado para RI: https://jaxb.java.net/guide/Performance_and_thread_safety.html (pero no Moxy AFAIK) – Caoilte

9

Es una pena que esto no se describa específicamente 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 clasificación, con un javadoc comment en el código que indica 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, ya que implica escanear clases y paquetes para anotaciones. Pero medirlo es la mejor manera de saberlo.

+0

Hola @JB, excelente respuesta, especialmente tus comentarios sobre la medición y por qué JAXBContext es costoso. – Vladimir

+1

Javadoc siempre ha sido débil en los hechos cruciales de _lifecycle_. Seguro que nos da una repetición trivial de getters & setters de propiedades, pero en cuanto a saber cómo/dónde obtener o crear una instancia, mutación y seguridad de hilos ... parece pasar por alto completamente esos factores más importantes. Suspiro :) –

29

Idealmente, debe tener un singleton JAXBContext e instancias locales de Marshaller y Unmarshaller.

JAXBContext casos son thread-safe mientras Marshaller y Unmarshaller casos son no thread-safe y nunca debe ser compartida a través de roscas.

+0

gracias por la respuesta. Desafortunadamente, tengo que seleccionar solo una respuesta :-) – Vladimir

0

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 de singleton -map.

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

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 se necesita es un poco de 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; 
    } 
} 
+8

ThreadLocal presentará otros problemas sutiles, sin beneficio. Solo conserve un solo JAXBContext (esa es la parte costosa) y cree un nuevo Unmarshaller cuando sea necesario. – ymajoros

4

He resuelto este problema utilizando hilo compartida segura JAXBContext e hilo locales ONU/marschallers (así que teóricamente, habrá tantas instancias un/marshaller como hilos hay que las accedieron) wi la sincronización th solo en un/marshaller 's inicialización.

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"); 
    } 
} 
+6

ThreadLocal presentará otros problemas sutiles, sin beneficio. Solo conserve un solo JAXBContext (esa es la parte costosa) y cree un nuevo Unmarshaller cuando sea necesario. – ymajoros

1

Even better !! En función de la buena solución de la publicación anterior, cree el contexto una sola vez en el constructor y guárdelo en lugar de la clase.

Cambie la línea:

private Class clazz; 

con éste:

private JAXBContext jc; 

Y el constructor principal con éste:

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

por lo que en la getMarshaller/getUnmarshaller puede quitar esta línea:

JAXBContext jc = JAXBContext.newInstance(clazz); 

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

+0

Qué tan grande era el archivo xml que estaba analizando. ¿Ves mejoras significativas con archivos xml muy grandes? – John

+0

no es realmente una cuestión de grandes archivos xml (las minas van desde solo 2-3kb hasta + 6mb), sino que se trata de una gran cantidad de archivos xml (estamos hablando aquí de aproximadamente 10,000 solicitudes por minuto) ; en ese caso, crear el contexto una sola vez ganando esos pequeños ms hace una gran diferencia – tbarderas

2

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

para evitar la sobrecarga implicada en la creación de una instancia de JAXBContext, una aplicación de JAXB es animado a reutilizar una instancia JAXBContext. Se requiere una implementación de de la clase abstracta JAXBContext para ser thread-safe, por lo tanto, los subprocesos múltiples en una aplicación pueden compartir la misma instancia de JAXBContext .

[..]

clase JAXBContext está diseñado para ser inmutable y por lo tanto para las hebras. Dada la cantidad de procesamiento dinámico que potencialmente podría tener lugar en 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 hilos de seguridad de Unmarshaller y Marshaller. Por lo tanto, es mejor asumir que no lo son.