web-dev-qa-db-ja.com

JAXBによるコンテキストとマーシャラーの作成コスト

質問は少し理論的ですが、JAXBコンテキスト、マーシャラー、アンマーシャラーを作成するコストはいくらですか?

私のコードは、各マーシャリングでコンテキストとマーシャラーを作成するのではなく、すべてのマーシャリング操作に対して同じJAXBコンテキストと、おそらく同じマーシャラーを保持することでメリットがあることがわかりました。

JAXBコンテキストとマーシャラー/アンマーシャラーを作成するコストはいくらですか?マーシャリング操作ごとにcontext + marshallerを作成しても大丈夫ですか、それを避ける方が良いですか?

106
Vladimir

注:私は EclipseLink JAXB(MOXy) リードおよびJAXB 2のメンバー( JSR-222 )エキスパートグループ。

JAXBContextはスレッドセーフであり、メタデータを複数回初期化するコストを回避するために、一度だけ作成して再利用する必要があります。 MarshallerおよびUnmarshallerはスレッドセーフではありませんが、作成が軽量であり、操作ごとに作成できます。

224
bdoughan

理想的には、シングルトンJAXBContextMarshallerおよびUnmarshallerのローカルインスタンスが必要です。

JAXBContextインスタンスはスレッドセーフですが、MarshallerおよびUnmarshallerインスタンスはnotスレッドセーフであり、スレッド間で共有しないでください。

37
Sahil Muthoo

これがjavadocで具体的に説明されていないのは残念です。私が言えることは、Springはスレッド間で共有されるグローバルなJAXBContextを使用するのに対して、JAXBマーシャラーは必ずしもスレッドではないことを示すコードで javadoc comment で、マーシャリング操作ごとに新しいマーシャラーを作成することです安全。

このページでも同じことが言えます: https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety

JAXBContextの作成は、クラスとパッケージのアノテーションのスキャンを伴うため、コストのかかる操作だと思います。しかし、それを測定することが知るための最良の方法です。

13
JB Nizet

共有スレッドセーフ--​​JAXBContextとスレッドローカルn/marschallersを使用してこの問題を解決しました(理論的には、多くのn/marshallerインスタンスにアクセスしたスレッドが存在するため)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 )には、「4.2 JAXBContext」セクションで次のように記述されています。

JAXBContext インスタンスの作成に伴うオーバーヘッドを避けるために、JAXBアプリケーションはJAXBContextインスタンスを再利用するよう奨励されています。抽象クラスJAXBContextの実装はスレッドセーフである必要があります。したがって、アプリケーション内の複数のスレッドは同じJAXBContextインスタンスを共有できます。

[..]

JAXBContextクラスは不変であるため、スレッドセーフになるように設計されています。 JAXBContxtの新しいインスタンスを作成するときに発生する可能性のある動的処理の量を考えると、アプリケーションのパフォーマンスを改善するために、JAXBContextインスタンスをスレッド間で共有し、可能な限り再利用することをお勧めします。

残念ながら、この仕様は Unmarshaller および Marshaller のスレッド安全性に関する主張をしていません。したがって、そうではないと想定するのが最善です。

3

さらに良い!!上記の投稿の適切なソリューションに基づいて、コンストラクターでコンテキストjust-onceを作成し、クラスの代わりに保存します。

次の行を置き換えます。

  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ミリ秒に短縮されます。

1
tbarderas

私は通常、ThreadLocalクラスパターンでこのような問題を解決します。クラスごとに異なるマーシャラーが必要であるという事実を考えると、singleton- mapパターンと組み合わせることができます。

作業時間を15分節約します。 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;
  }
}
0
bvdb