TOP > 技術メモ > Java > JAXB

JAXB (Java Architecture for XML Binding)

JAXBの性能測定(コスト)について

JAXBの変換処理は1000回実行して測定したところ、以下の「*」がボトルネックになっている。
今回はシングルスレッドで実行しているため、マルチスレッド環境では排他処理もコストになってくる。

					実行回数:1000
					実行にかかった時間は 3698 ミリ秒です。
					以下内訳 ----------------------------------------
					JAXBContext.newInstanceにかかった時間は 2103 ミリ秒です。*1
					createMarshallerにかかった時間は 0 ミリ秒です。
					Schema.newInstanceにかかった時間は 233 ミリ秒です。
					newSchemaにかかった時間は 1099 ミリ秒です。*2
					setSchemaにかかった時間は 0 ミリ秒です。
					marshalにかかった時間は 263 ミリ秒です。*3
					
上記でプールして使いまわしたほうが良い箇所は、
*1:JAXBContext.newInstance の戻り値
このクラスはスレッドセーフのため、マルチスレッドから実行しても影響がない。
JAXBContextクラスを使いまわして都度createMarshaller や createUnmarshaller をするのも良い。
後述するが、Jarファイル数(クラス数)が多い場合や、セキュリティマネージャを使用している場合は、
Marshall や Unmarshall を使いまわした方が「速い」(特に Web で Spring Framework を使っている場合)。
これで大分ハマリました。JAX~系の設計がおかしい気がする。
*2:Schema.newSchema の戻り値
スキーマ検証するためのオブジェクト。非スレッドセーフなため排他が必要。
このスキーマ検証(setSchema後の変換処理)が、無茶苦茶コストがでかかったりする。
個人的には、スキーマ検証はデバッグ用だと思っている。(XML変換時に検証が必要な設計自体に疑問)
XML化する前のオブジェクトで妥当性を保証すれば良い。

*3:Marshal, Unmarshal
XML変換を行うクラス(マーシャルは「XML→Object」、アンマーシャルは「Object→XML」)。
スキーマ検証を行う場合、都度createしていても、setSchemaのオブジェクトが「非スレッドセーフ」のため注意しないといけない。
また、unmarshallを実行すると、実際にはImplクラスの内部で XMLReader を生成し(これが厄介)、メンバ変数に保持し使用される。
2回目以降は、初回に生成した XMLReader を再利用するという仕組み。
上記のため、都度 createUnmarshall を行うと、SAXParserFactory と XMLReader の作成が毎回行われてしまう。
SAXParserFactoryの作成は、クラスローダにも依存するが、JarFileFactory.getメソッド(排他されている)を実行する。
また、Spring Framework や Javaリフレクション も、内部では JarFileFactory.getメソッドを実行しているため、排他が発生してしまう。
そのため、Umarshall はキャッシュして使いまわした方が性能が出る可能性がある(JarFileFactory.getをさせない)。

以下、今回の計測に使ったサンプルクラス。

					package fomsan.ne.jp.example.jaxbtest;
					
					// import 省略
					
					/**
					 * 性能測定サンプルクラス
					 */
					public class SampleJAXB
					{
					
						// 変換対象
						private static TargetObject target = new TargetObject();
						static
						{
							// TargetObject初期化(割愛)
						}
					
					
						private static long sumNewInstance = 0;
						private static long sumCreateMarshal = 0;
						private static long sumCreateSchemaFactory = 0;
						private static long sumNewSchema = 0;
						private static long sumSetSchema = 0;
						private static long sumMarshall = 0;
						private static long sumTotal = 0;
					
						/** 計測メインクラス */
						public static void main(String[] args) {
							int i = 0;
							// 1000会実行
							for (; i < 1000; i++) {
								targetProc();
							}
					
							System.out.println("実行回数:" + i);
							System.out.println("実行にかかった時間は " + sumTotal + " ミリ秒です。");
							System.out.println("以下内訳 ----------------------------------------");
							System.out.println("JAXBContext.newInstanceにかかった時間は "
									+ sumNewInstance + " ミリ秒です。");
							System.out.println("createMarshallerにかかった時間は "
									+ (sumCreateMarshal) + " ミリ秒です。");
							System.out.println("Schema.newInstanceにかかった時間は "
									+ sumCreateSchemaFactory + " ミリ秒です。");
							System.out.println("newSchemaにかかった時間は "
									+ sumNewSchema + " ミリ秒です。");
							System.out.println("setSchemaにかかった時間は "
									+ sumSetSchema + " ミリ秒です。");
							System.out.println("marshalにかかった時間は "
									+ sumMarshall + " ミリ秒です。");
						}
					
					
						/** 測定 */
						private static void targetProc() {
							long tmp = 0;
							ByteArrayOutputStream baos = null;
							long start = System.currentTimeMillis();
							try {
								/** ★JAXBContextを生成する */
								long jaxb = System.currentTimeMillis();
								
								// JAXBContextの生成
								final JAXBContext jc = JAXBContext.newInstance(TargetObject.class);
					
								long stopJaxb = System.currentTimeMillis();
								tmp = stopJaxb - jaxb;
								sumNewInstance += tmp;
					
					
								/** ★Marshaller(Object→XML変換用)クラスを生成する */
								long createMar = System.currentTimeMillis();
					
								final Marshaller mar = jc.createMarshaller();
					
								long stopCreateMar = System.currentTimeMillis();
								tmp = stopCreateMar - createMar;
								sumCreateMarshal += tmp;
					
					
								/** ★スキーマファクトリーを生成する */
								long createSchemaFactory = System.currentTimeMillis();
					
								final SchemaFactory factory = SchemaFactory.newInstance(
										XMLConstants.W3C_XML_SCHEMA_NS_URI);
					
								long stopCreateSchemaFactory = System.currentTimeMillis();
								tmp = stopCreateSchemaFactory - createSchemaFactory;
								sumCreateSchemaFactory += tmp;
					
					
								/** ★Object→XML変換時の検証クラス生成 */
								long newSchema = System.currentTimeMillis();
					
								final Schema sc = factory.newSchema(new File("target_schema.xsd"));
					
								long stopNewSchema = System.currentTimeMillis();
								tmp = stopNewSchema - newSchema;
								sumNewSchema += tmp;
					
					
								/** ★セットスキーマ */
								long setSchema = System.currentTimeMillis();
					
								mar.setSchema(sc);
					
								long stopSetSchema = System.currentTimeMillis();
								tmp = stopSetSchema - setSchema;
								sumSetSchema += tmp;
					
					
								/** ★マーシャル処理時間 */
								long marshal = System.currentTimeMillis();
					
								baos = new ByteArrayOutputStream();
								// 同一コンテキストから作った変換クラスのmarshal、unmarshalは排他が必要
								// synchonized (jc) {
								mar.marshal(target, baos);
								// }
					
								long stopMarshal = System.currentTimeMillis();
								tmp = stopMarshal - marshal;
								sumMarshall += tmp;
					
							} catch (JAXBException e) 0{
								e.printStackTrace();
							} catch (SAXException e) {
								e.printStackTrace();
							} finally {
								if (baos != null) {
									try {
										baos.close();
									} catch (IOException e) {
										e.printStackTrace();
									}
								}
							}
							/** 実行時間の合計 */
							long stop = System.currentTimeMillis();
							tmp = stop - start;
							sumTotal += tmp;
						}
					}
					

▲ページの先頭へ