Trasis Inc.

渋谷拠点のシステム開発会社

SimpleDateFormat性能&シリアライズ調査

結論

java.text.SimpleDateFormat の使用にはいくつかの注意が必要である。

  • SimpleDateFormat はスレッド・セーフではない。複数のスレッドで同時に使用してはならない。
  • SimpleDateFormat はシリアライズ可能であるが、シリアライズすべきではない。
  • 利用ケースによるが、おそらくほとんどのケースにおいて SimpleDateFormat を毎回生成せず、static で一度だけ生成し、同期を取りながら使用したほうが高速である。

調査に使った Java のバージョン: Sun JDK 1.6.0_05

SimpleDateFormat はシリアライズできてしまうが…

SimpleDateFormat の親クラスが java.io.Serializable を実装しているため、シリアライズできてしまう。
しかし、シリアライズしてみると、出力データサイズが 37 kB とかなり大きなサイズになってしまう。

テストプログラム

package jp.trasis.test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.text.SimpleDateFormat;
public class Test1 {
public static void main(String[] args) throws IOException {
SimpleDateFormat obj = new SimpleDateFormat("yyyy-MM-dd");
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bout);
out.writeObject(obj);
out.close();
byte[] data = bout.toByteArray();
System.out.println(data.length);
}
}

これは、SimpleDateFormat の中で java.text.DateFormatSymbols インスタンスを保持している。このクラスは月、曜日、タイムゾーンデータなど、地域対応が可能な日付/時刻フォーマットデータを保持しているため、かなり大きなデータ量となる。

自分で明示的に SimpleDateFormat に格納しているならともかく、通常のケースでこれをシリアライズする必要はない。

オブジェクト生成コスト vs 同期コスト

次の2つのプログラムを考えてみる。どちらが高速に動作するだろうか?

code-1: 実行のたびに SimpleDateFormat オブジェクトを生成して利用するケース。

実行するたびに オブジェクト生成コストがかかっている。

package jp.trasis.speedTest.simpleDateFormat;
import java.text.SimpleDateFormat;
public class Sample1 {
public String toString(long t) {
return new SimpleDateFormat("yyyy/MM/dd").format(t);
}
public static void main(String[] args) {
long t = System.currentTimeMillis();
Sample1 obj = new Sample1();
String s = obj.toString(t);
System.out.println(s);
}
}

code-2: static で一度だけ SimpleDateFormat オブジェクトを生成するケース

利用する際に synchronized で同期をとって利用する。
同期コストがかかっている。

package jp.trasis.speedTest.simpleDateFormat;
import java.text.SimpleDateFormat;
public class Sample2 {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
public String toString(long t) {
synchronized (sdf) {
return sdf.format(t);
}
}
public static void main(String[] args) {
long t = System.currentTimeMillis();
Sample2 obj = new Sample2();
String s = obj.toString(t);
System.out.println(s);
}
}

性能測定結果

いずれのケースでも code-2 の方が高速に動作した。
なお、テストで利用したマシンは CPU が Core2duo であるため、2 スレッドで実行できる分、複数スレッドで実行した場合、 code-1 は高速化している。

スレッド数 code-1 code-2
プログラム 実行時間
[msec]
プログラム 実行時間
[msec]
1 Test1_1 5656 Test2_1 796
10 Test1_2 3359 Test2_2 937
100 Test1_3 3625 Test2_3 1204

static に生成して利用するのはガベージ・コレクションの観点からも優位である。
SimpleDateFormat オブジェクトを毎回生成するということは、生存時間の短いオブジェクトをより多く発生することになり、GC 頻度を高めることになる。

サンプルコード

test-SimpleDateFormat.zip