Kryo
シリアライゼーションライブラリ
Kryo
概要
Kryoは、Java向けの高速で効率的なバイナリオブジェクトグラフシリアライゼーションフレームワークです。高速な処理速度、小さいサイズ、使いやすいAPIを目標としており、オブジェクトをファイル、データベース、ネットワーク経由で永続化する必要がある場合に有用です。
詳細
Kryoは、標準のJavaシリアライゼーションと比較して大幅に高いパフォーマンスを提供し、より小さなシリアライズ済みデータを生成します。自動的なディープコピーとシャローコピーをサポートし、複数の参照や循環参照を適切に処理できます。
主な特徴
- 高速処理: 標準Javaシリアライゼーションより大幅に高速
- コンパクトな出力: シリアライズされたデータサイズが小さい
- 自動シリアライゼーション: プリミティブ、リスト、マップ、Enumなどのデフォルトシリアライザーを提供
- カスタムシリアライザー: 特定のクラスに対してカスタムシリアライゼーションプロセスを定義可能
- 複雑なオブジェクトグラフ対応: 循環参照やネストされたオブジェクトを適切に処理
- スレッドセーフでない: 高パフォーマンスのためスレッドセーフではない設計
- プラガブルアーキテクチャ: 異なるシリアライザーを柔軟に組み込み可能
使用されている環境
- ビッグデータ処理: Apache Sparkなどのフレームワークで分散ノード間のデータシリアライゼーション
- ネットワーク通信: 高速なデータ転送が必要なアプリケーション
- キャッシング: キャッシュソリューションでの高速なオブジェクトシリアライゼーション
- 永続ストレージ: 大量のデータを効率的に保存するアプリケーション
メリット・デメリット
メリット
- 高速な処理速度: 標準Javaシリアライゼーションと比較して50%以上高速
- 小さなペイロードサイズ: 圧縮無効時でも平均61%、圧縮有効時で80%のサイズ削減
- 柔軟なシリアライゼーション:
java.io.Serializableを実装していないクラスもシリアライズ可能 - 成熟した実装: Twitter、Groupon、Yahoo、Hive、Stormなど多くの本番環境で採用
- 簡単なAPI: 直感的で使いやすいAPIデザイン
- オブジェクトコピー機能: ディープコピー・シャローコピーを簡単に実装可能
デメリット
- バージョン互換性の欠如: リリース間でのシリアライゼーション互換性が保証されない
- 長期保存に不適: データの長期保存には推奨されない
- スレッドセーフでない: マルチスレッド環境では適切な同期処理やプーリングが必要
- 登録の必要性: 最高のパフォーマンスを得るにはクラスの事前登録が必要
- 引数なしコンストラクタ必要: 最適なパフォーマンスのために引数なしコンストラクタが必要
参考ページ
書き方の例
基本的な使い方
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.*;
public class KryoExample {
public static void main(String[] args) throws Exception {
Kryo kryo = new Kryo();
// クラスの登録(パフォーマンス向上のため)
kryo.register(Person.class);
Person person = new Person("田中太郎", 30);
// ファイルにシリアライズ
Output output = new Output(new FileOutputStream("person.bin"));
kryo.writeObject(output, person);
output.close();
// ファイルからデシリアライズ
Input input = new Input(new FileInputStream("person.bin"));
Person restoredPerson = kryo.readObject(input, Person.class);
input.close();
System.out.println(restoredPerson);
}
static class Person {
String name;
int age;
// Kryoには引数なしコンストラクタが必要
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
}
クラスが不明な場合のシリアライゼーション
// クラス情報と一緒に書き込み
kryo.writeClassAndObject(output, someObject);
// クラス情報と一緒に読み込み
Object object = kryo.readClassAndObject(input);
// nullの可能性がある場合
kryo.writeObjectOrNull(output, maybeNullObject);
SomeClass object = kryo.readObjectOrNull(input, SomeClass.class);
カスタムシリアライザーの実装
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.time.LocalDateTime;
public class LocalDateTimeSerializer extends Serializer<LocalDateTime> {
@Override
public void write(Kryo kryo, Output output, LocalDateTime dateTime) {
output.writeLong(dateTime.toEpochSecond(java.time.ZoneOffset.UTC));
output.writeInt(dateTime.getNano());
}
@Override
public LocalDateTime read(Kryo kryo, Input input, Class<LocalDateTime> type) {
long epochSecond = input.readLong();
int nano = input.readInt();
return LocalDateTime.ofEpochSecond(epochSecond, nano, java.time.ZoneOffset.UTC);
}
}
// 使用例
Kryo kryo = new Kryo();
kryo.register(LocalDateTime.class, new LocalDateTimeSerializer());
スレッドセーフな使用(プーリング)
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.util.Pool;
public class KryoPool {
private static final Pool<Kryo> kryoPool = new Pool<Kryo>(true, false, 8) {
protected Kryo create() {
Kryo kryo = new Kryo();
// 必要なクラスを登録
kryo.register(Person.class);
kryo.register(Address.class);
// その他の設定
return kryo;
}
};
public static byte[] serialize(Object object) {
Kryo kryo = kryoPool.obtain();
try (Output output = new Output(1024, -1)) {
kryo.writeClassAndObject(output, object);
return output.toBytes();
} finally {
kryoPool.free(kryo);
}
}
public static Object deserialize(byte[] bytes) {
Kryo kryo = kryoPool.obtain();
try (Input input = new Input(bytes)) {
return kryo.readClassAndObject(input);
} finally {
kryoPool.free(kryo);
}
}
}
参照の有効化/無効化
Kryo kryo = new Kryo();
// 参照を有効化(デフォルト)- 循環参照を処理できるが遅い
kryo.setReferences(true);
// 参照を無効化 - 高速だが循環参照でスタックオーバーフロー
kryo.setReferences(false);
// 特定のシリアライザーのみ参照を無効化
kryo.register(SomeClass.class, new FieldSerializer(kryo, SomeClass.class) {{
setReferences(false);
}});
オブジェクトのコピー
Kryo kryo = new Kryo();
// ディープコピー
Person originalPerson = new Person("山田花子", 25);
Person deepCopy = kryo.copy(originalPerson);
// シャローコピー
Person shallowCopy = kryo.copyShallow(originalPerson);
// コピー時の設定
kryo.setCopyReferences(true); // 参照を保持
kryo.setCopyTransient(false); // transientフィールドをコピーしない
Maven依存関係
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.6.2</version>
</dependency>
Gradle依存関係
implementation 'com.esotericsoftware:kryo:5.6.2'