JAXB (Java Architecture for XML Binding)
- JAXBの性能測定(コスト)について [2013-08-20]
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 を使っている場合)。
*2:Schema.newSchema の戻り値
スキーマ検証するためのオブジェクト。非スレッドセーフなため排他が必要。
このスキーマ検証(setSchema後の変換処理)が、無茶苦茶コストがでかかったりする。
*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; } }