JAXB作成コンテキストおよびマーシャラーのコスト


120

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

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

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

回答:


244

注: 私はEclipseLink JAXB(MOXy)のリーダーであり、JAXB 2(JSR-222)エキスパートグループのメンバーです。

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


7
素晴らしい答え。JAXBをリードした経験から、自信が持てるようになりました。
ウラジミール

7
私はあなたを信頼していますが、これはドキュメントのどこかにありますか?
フルダ2014年

3
それはRIのために文書化されています:jaxb.java.net/guide/Performance_and_thread_safety.html(Moxy AFAIKは除く)
Caoilte 2014

39
これをJavadocで指定してください。これらの重要な側面を文書化しないことは十分ではありません。
トーマスW

6
JAXBContextがスレッドセーフであることは、Javadocでは言及されていません。また、JAXBの使用方法のほぼすべての例で、JAXBを一度作成してスレッド間で共有する必要があることについては触れていません。その結果、私は現在、ライブシステムからさらに別のハウリングリソースリークを削除しています。Grrrrrrr。
Reg Whitton 2016

42

理想的には、シングルトンが必要JAXBContextとのローカルインスタンスMarshallerとをUnmarshaller

JAXBContextインスタンスはスレッドセーフしばらくしているMarshallerUnmarshallerインスタンスがありませんスレッドセーフおよびスレッド間で共有すべきではありません。


答えてくれてありがとう。残念ながら、私は1つだけ回答を選択する必要があります:-)
Vladimir

15

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

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

JAXBContextの作成は、クラスとパッケージの注釈をスキャンする必要があるため、コストのかかる操作だと思います。しかし、それを測定することは知る最良の方法です。


こんにちは@JB、特に測定に関するコメントとJAXBContextが高価な理由についての素晴らしい回答
ウラジミール

1
Javadocは常に、ライフサイクルの重要な事実に弱くなっています。プロパティのゲッターとセッターを簡単に繰り返すことができますが、インスタンスを取得または作成する方法/場所、ミューテーション、およびスレッドセーフを知ることに関しては、これらの最も重要な要素を完全に見逃しているようです。ため息:)
トーマスW

4

JAXB 2.2(JSR-222)には、「4.2 JAXBContext」セクションで次のように書かれています。

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

[..]

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

残念ながら、この仕様では、Unmarshallerとのスレッドセーフ性に関する主張は行われていませんMarshaller。したがって、そうではないと想定するのが最善です。


3

私はこの問題を使用して解決しました:

  • 共有スレッドセーフJAXBContextおよびスレッドローカルun / marschallers
  • (したがって、理論的には、un / marshallerインスタンスは、それらにアクセスしたスレッドの数と同じ数になります)
  • un / marshallerの初期化でのみ同期を行います。
public class MyClassConstructor {
    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");
        }
    }
}

8
ThreadLocalは他のサブタイルの問題をもたらしますが、利益はありません。単一のJAXBContext(コストのかかる部分)を保持し、必要に応じて新しいUnmarshallerを作成するだけです。
ymajoros 2014

2

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

次の行を置き換えます。

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


解析していたxmlファイルの大きさ。非常に大きなxmlファイルで大幅な改善が見られますか?
John

1
それは実際には大きなxmlファイルの問題ではありません(鉱山は2-3kbから+ 6mbまでです)代わりに、非常に多数のxmlファイルの問題です(ここでは1分あたり約10,000のxmlリクエストについて話しています)。その場合、これらの小さなmsを一度取得するだけでコンテキストを作成すると、大きな違いが生じます
tbarderas

1

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

作業時間を15分節約できます。Jaxbマーシャラーとアンマーシャラー用のスレッドセーフなファクトリーの実装を次に示します。

次のようにインスタンスにアクセスできます...

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;
  }
}

10
ThreadLocalは他のサブタイルの問題をもたらしますが、利益はありません。単一のJAXBContext(コストのかかる部分)を保持し、必要に応じて新しいUnmarshallerを作成するだけです。
ymajoros 2014

複数のクラスを渡すことができるため、実際には個別のJAXBContextは必要ありません。したがって、マーシャリングされるクラスを予測できる場合は、単一の共有クラスを作成できます。また、JAXB仕様では、それらがすでにスレッドセーフである必要があります。
MauganRa
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.