it-swarm.it

JAXB creando il contesto e il costo dei marshaller

La domanda è un po 'teorica, qual è il costo di creazione del contesto JAXB, marshaller e unmarshaller?

Ho scoperto che il mio codice potrebbe trarre vantaggio dal mantenere lo stesso contesto JAXB e possibilmente lo stesso marshaller per tutte le operazioni di marshalling piuttosto che creare contesto e marshaller su ogni marshalling.

Quindi, qual è il costo di creazione del contesto JAXB e marshaller/unmarshaller? Va bene creare un contesto + marshaller per ogni operazione di marshalling o è meglio evitarlo?

106
Vladimir

Nota: Sono il EclipseLink JAXB (MOXy) lead e un membro del gruppo di esperti JAXB 2 ( JSR-222 ).

JAXBContext è thread-safe e deve essere creato una sola volta e riutilizzato per evitare il costo di inizializzazione dei metadati più volte. Marshaller e Unmarshaller non sono thread-safe, ma sono leggeri da creare e potrebbero essere creati per operazione.

224
bdoughan

Idealmente, dovresti avere un singleton JAXBContext e istanze locali di Marshaller e Unmarshaller.

Le istanze JAXBContext sono thread-safe mentre le istanze Marshaller e Unmarshaller sono not thread-safe e non devono mai essere condivise tra thread.

37
Sahil Muthoo

È un peccato che questo non sia specificamente descritto nel javadoc. Quello che posso dire è che Spring usa un JAXBContext globale, condiviso tra thread, mentre crea un nuovo marshaller per ogni operazione di marshalling, con un commento javadoc nel codice che dice che i marshaller JAXB non sono necessariamente thread- sicuro.

Lo stesso si dice in questa pagina: https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety .

Immagino che la creazione di un JAXBContext sia un'operazione costosa, poiché comporta la scansione di classi e pacchetti per le annotazioni. Ma misurarlo è il modo migliore per saperlo.

13
JB Nizet

Ho risolto questo problema usando il thread condiviso sicuro JAXBContext e il thread locale n/marschallers (quindi teoricamente, ci saranno tanti n/marshaller istanze in quanto vi sono thread che vi hanno avuto accesso) con sincronizzazione solo sull'inizializzazione di n/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");
    }
}
3
peeeto

JAXB 2.2 ( JSR-222 ) ha questo da dire, nella sezione "4.2 JAXBContext":

Per evitare il sovraccarico legato alla creazione di un'istanza JAXBContext , un'applicazione JAXB è incoraggiata a riutilizzare un'istanza JAXBContext . Un'implementazione della classe astratta JAXBContext deve essere thread-safe , pertanto più thread in un'applicazione possono condividere la stessa istanza JAXBContext.

[..]

La classe JAXBContext è progettata per essere immutabile e quindi sicura per i thread. Data la quantità di elaborazione dinamica che potenzialmente potrebbe aver luogo durante la creazione di una nuova istanza di JAXBContxt, si consiglia di condividere un'istanza JAXBContext tra thread e riutilizzarla il più possibile per migliorare le prestazioni dell'applicazione.

Sfortunatamente, la specifica non fa affermazioni riguardo la sicurezza del thread di Unmarshaller e Marshaller . Quindi è meglio supporre che non lo siano.

3
Martin Andersson

Anche meglio!! Sulla base della buona soluzione del post sopra, crea il contesto solo una volta nel costruttore e salvalo al posto della classe.

Sostituisci la linea:

  private Class clazz;

con questo:

  private JAXBContext jc;

E il costruttore principale con questo:

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

quindi in getMarshaller/getUnmarshaller è possibile rimuovere questa riga:

  JAXBContext jc = JAXBContext.newInstance(clazz);

Questo miglioramento rende, nel mio caso, che i tempi di elaborazione scendono da 60 ~ 70ms a soli 5 ~ 10ms

1
tbarderas

Solitamente risolvo problemi come questo con un modello di classe ThreadLocal. Dato che è necessario un marshaller diverso per ogni classe, è possibile combinarlo con un modello singleton-map.

Per risparmiare 15 minuti di lavoro. Qui segue la mia implementazione di una Factory thread-safe per Jaxb Marshallers e Unmarshallers.

Ti consente di accedere alle istanze come segue ...

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

E il codice di cui avrai bisogno è una piccola classe Jaxb che si presenta come segue:

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
bvdb