Kryo adalah kerangka serialisasi grafik objek biner yang cepat dan efisien untuk Java. Tujuan dari proyek ini adalah kecepatan tinggi, ukuran rendah, dan API yang mudah digunakan. Proyek ini berguna setiap kali objek perlu dipertahankan, baik ke file, database, atau melalui jaringan.
Kryo juga dapat melakukan penyalinan/kloning otomatis dalam dan dangkal. Ini adalah penyalinan langsung dari objek ke objek, bukan objek ke byte ke objek.
Dokumentasi ini untuk Kryo versi 5.x. Lihat Wiki untuk versi 4.x.
Silakan gunakan milis Kryo untuk pertanyaan, diskusi, dan dukungan. Harap batasi penggunaan pelacak masalah Kryo pada bug dan penyempurnaan, bukan pertanyaan, diskusi, atau dukungan.
Kryo menerbitkan dua jenis artefak/toples:
Kryo JAR tersedia di halaman rilis dan di Maven Central. Snapshot terbaru Kryo, termasuk snapshot build master, ada di Repositori Sonatype.
Untuk menggunakan rilis Kryo terbaru di aplikasi Anda, gunakan entri ketergantungan ini di pom.xml
Anda :
< dependency >
< groupId >com.esotericsoftware</ groupId >
< artifactId >kryo</ artifactId >
< version >5.6.2</ version >
</ dependency >
Untuk menggunakan rilis Kryo terbaru di perpustakaan yang ingin Anda terbitkan, gunakan entri ketergantungan ini di pom.xml
Anda:
< dependency >
< groupId >com.esotericsoftware.kryo</ groupId >
< artifactId >kryo5</ artifactId >
< version >5.6.2</ version >
</ dependency >
Untuk menggunakan snapshot Kryo terbaru, gunakan:
< repository >
< id >sonatype-snapshots</ id >
< name >sonatype snapshots repo</ name >
< url >https://oss.sonatype.org/content/repositories/snapshots</ url >
</ repository >
<!-- for usage in an application: -->
< dependency >
< groupId >com.esotericsoftware</ groupId >
< artifactId >kryo</ artifactId >
< version >5.6.3-SNAPSHOT</ version >
</ dependency >
<!-- for usage in a library that should be published: -->
< dependency >
< groupId >com.esotericsoftware.kryo</ groupId >
< artifactId >kryo5</ artifactId >
< version >5.6.3-SNAPSHOT</ version >
</ dependency >
Tidak semua orang adalah penggemar Maven. Menggunakan Kryo tanpa Maven memerlukan penempatan Kryo JAR di classpath Anda bersama dengan JAR ketergantungan yang ditemukan di lib.
Membangun Kryo dari sumber memerlukan JDK11+ dan Maven. Untuk membangun semua artefak, jalankan:
mvn clean && mvn install
Melompat ke depan untuk menunjukkan bagaimana perpustakaan dapat digunakan:
import com . esotericsoftware . kryo . Kryo ;
import com . esotericsoftware . kryo . io . Input ;
import com . esotericsoftware . kryo . io . Output ;
import java . io .*;
public class HelloKryo {
static public void main ( String [] args ) throws Exception {
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class );
SomeClass object = new SomeClass ();
object . value = "Hello Kryo!" ;
Output output = new Output ( new FileOutputStream ( "file.bin" ));
kryo . writeObject ( output , object );
output . close ();
Input input = new Input ( new FileInputStream ( "file.bin" ));
SomeClass object2 = kryo . readObject ( input , SomeClass . class );
input . close ();
}
static public class SomeClass {
String value ;
}
}
Kelas Kryo melakukan serialisasi secara otomatis. Kelas Output dan Input menangani buffering byte dan secara opsional melakukan pembilasan ke aliran.
Sisa dokumen ini merinci cara kerjanya dan penggunaan perpustakaan tingkat lanjut.
Mendapatkan data masuk dan keluar dari Kryo dilakukan menggunakan kelas Input dan Output. Kelas-kelas ini tidak aman untuk thread.
Kelas Output adalah OutputStream yang menulis data ke buffer array byte. Buffer ini dapat diperoleh dan digunakan secara langsung, jika diinginkan array byte. Jika Output diberi OutputStream, ia akan membuang byte ke aliran ketika buffer menjadi penuh, jika tidak, Output dapat menambah buffernya secara otomatis. Output memiliki banyak metode untuk menulis primitif dan string ke byte secara efisien. Ini menyediakan fungsionalitas yang mirip dengan DataOutputStream, BufferedOutputStream, FilterOutputStream, dan ByteArrayOutputStream, semuanya dalam satu kelas.
Tip: Output dan Input menyediakan semua fungsi ByteArrayOutputStream. Jarang ada alasan untuk memasukkan Output ke ByteArrayOutputStream.
Output mem-buffer byte ketika menulis ke OutputStream, jadi flush
atau close
harus dipanggil setelah penulisan selesai agar byte yang di-buffer ditulis ke OutputStream. Jika Output belum diberikan OutputStream, pemanggilan flush
atau close
tidak diperlukan. Tidak seperti banyak aliran, instance Output dapat digunakan kembali dengan mengatur posisi, atau mengatur array atau aliran byte baru.
Tip: Karena Output sudah buffer, tidak ada alasan untuk memasukkan Output ke BufferedOutputStream.
Argumen nol Konstruktor Output menciptakan Output yang tidak diinisialisasi. Output setBuffer
harus dipanggil sebelum Output dapat digunakan.
Kelas Input adalah InputStream yang membaca data dari buffer array byte. Buffer ini dapat diatur secara langsung, jika diinginkan pembacaan dari array byte. Jika Input diberi InputStream, maka buffer akan terisi dari stream ketika semua data di buffer telah dibaca. Input memiliki banyak metode untuk membaca primitif dan string dari byte secara efisien. Ini menyediakan fungsionalitas yang mirip dengan DataInputStream, BufferedInputStream, FilterInputStream, dan ByteArrayInputStream, semuanya dalam satu kelas.
Tip: Input menyediakan semua fungsi ByteArrayInputStream. Jarang ada alasan untuk meminta Input dibaca dari ByteArrayInputStream.
Jika Input close
dipanggil, InputStream Input ditutup, jika ada. Jika tidak membaca dari InputStream maka tidak perlu menelepon close
. Tidak seperti banyak aliran, instance Input dapat digunakan kembali dengan mengatur posisi dan batas, atau mengatur array byte baru atau InputStream.
Konstruktor Input argumen nol menciptakan Input yang tidak diinisialisasi. Input setBuffer
harus dipanggil sebelum Input dapat digunakan.
Kelas ByteBufferOutput dan ByteBufferInput bekerja persis seperti Output dan Input, kecuali mereka menggunakan ByteBuffer dan bukan array byte.
Kelas UnsafeOutput, UnsafeInput, UnsafeByteBufferOutput, dan UnsafeByteBufferInput bekerja persis seperti kelas non-unsafe, kecuali mereka menggunakan sun.misc.Unsafe untuk kinerja yang lebih tinggi dalam banyak kasus. Untuk menggunakan kelas-kelas ini, Util.unsafe
harus benar.
Kelemahan dari penggunaan buffer yang tidak aman adalah endianness asli dan representasi tipe numerik dari sistem yang melakukan serialisasi mempengaruhi data serial. Misalnya, deserialisasi akan gagal jika data ditulis di X86 dan dibaca di SPARC. Selain itu, jika data ditulis dengan buffer yang tidak aman, maka harus dibaca dengan buffer yang tidak aman.
Perbedaan kinerja terbesar dengan buffer yang tidak aman adalah dengan array primitif yang besar ketika pengkodean panjang variabel tidak digunakan. Pengkodean panjang variabel dapat dinonaktifkan untuk buffer yang tidak aman atau hanya untuk bidang tertentu (saat menggunakan FieldSerializer).
Kelas IO menyediakan metode untuk membaca dan menulis nilai panjang variabel int (varint) dan panjang (varlong). Hal ini dilakukan dengan menggunakan bit ke-8 dari setiap byte untuk menunjukkan apakah ada lebih banyak byte yang mengikuti, yang berarti varint menggunakan 1-5 byte dan varlong menggunakan 1-9 byte. Menggunakan pengkodean panjang variabel lebih mahal tetapi membuat data serial jauh lebih kecil.
Saat menulis nilai panjang variabel, nilai tersebut dapat dioptimalkan untuk nilai positif atau nilai negatif dan positif. Misalnya, ketika dioptimalkan untuk nilai positif, 0 hingga 127 ditulis dalam satu byte, 128 hingga 16383 dalam dua byte, dan seterusnya. Namun, angka negatif kecil adalah kasus terburuk pada 5 byte. Jika tidak dioptimalkan untuk hal positif, rentang ini akan diturunkan setengahnya. Misalnya, -64 hingga 63 ditulis dalam satu byte, 64 hingga 8191 dan -65 hingga -8192 dalam dua byte, dan seterusnya.
Buffer Input dan Output menyediakan metode untuk membaca dan menulis nilai berukuran tetap atau panjang variabel. Ada juga metode yang memungkinkan buffer memutuskan apakah nilai ukuran tetap atau panjang variabel ditulis. Hal ini memungkinkan kode serialisasi untuk memastikan pengkodean panjang variabel digunakan untuk nilai yang sangat umum yang akan menggembungkan keluaran jika ukuran tetap digunakan, sambil tetap mengizinkan konfigurasi buffer untuk memutuskan semua nilai lainnya.
Metode | Keterangan |
---|---|
tulisInt(int) | Menulis int 4 byte. |
tulisVarInt(int, boolean) | Menulis int 1-5 byte. |
tulisInt(int, boolean) | Menulis int 4 atau 1-5 byte (buffer memutuskan). |
menulisPanjang(panjang) | Menulis sepanjang 8 byte. |
writeVarLong(panjang, boolean) | Menulis sepanjang 1-9 byte. |
writeLong(panjang, boolean) | Menulis dengan panjang 8 atau 1-9 byte (buffer memutuskan). |
Untuk menonaktifkan pengkodean panjang variabel untuk semua nilai, metode writeVarInt
, writeVarLong
, readVarInt
, dan readVarLong
perlu diganti.
Akan berguna untuk menulis panjang beberapa data, lalu datanya. Apabila panjang suatu data tidak diketahui sebelumnya, maka semua data perlu di-buffer untuk menentukan panjangnya, baru dapat ditulis panjangnya, barulah datanya. menggunakan buffer tunggal yang besar untuk hal ini akan mencegah streaming dan mungkin memerlukan buffer yang terlalu besar, dan ini tidak ideal.
Pengkodean potongan memecahkan masalah ini dengan menggunakan buffer kecil. Ketika buffer penuh, panjangnya ditulis, lalu datanya. Ini adalah satu bagian data. Buffer dihapus dan ini berlanjut hingga tidak ada lagi data untuk ditulis. Potongan dengan panjang nol menunjukkan akhir dari potongan.
Kryo menyediakan kelas untuk membuat pengkodean potongan mudah digunakan. OutputChunked digunakan untuk menulis data yang dipotong. Ini memperluas Output, begitu pula semua metode mudah untuk menulis data. Ketika buffer OutputChunked penuh, ia akan memindahkan potongan tersebut ke OutputStream lain. Metode endChunk
digunakan untuk menandai akhir dari sekumpulan potongan.
OutputStream outputStream = new FileOutputStream ( "file.bin" );
OutputChunked output = new OutputChunked ( outputStream , 1024 );
// Write data to output...
output . endChunk ();
// Write more data to output...
output . endChunk ();
// Write even more data to output...
output . endChunk ();
output . close ();
Untuk membaca data yang dipotong, InputChunked digunakan. Ini memperluas Input, begitu pula semua metode mudah untuk membaca data. Saat membaca, InputChunked akan tampak mencapai akhir data ketika mencapai akhir kumpulan potongan. Metode nextChunks
melanjutkan ke kumpulan bongkahan berikutnya, meskipun tidak semua data telah dibaca dari kumpulan bongkahan saat ini.
InputStream outputStream = new FileInputStream ( "file.bin" );
InputChunked input = new InputChunked ( inputStream , 1024 );
// Read data from first set of chunks...
input . nextChunks ();
// Read data from second set of chunks...
input . nextChunks ();
// Read data from third set of chunks...
input . close ();
Umumnya Output dan Input memberikan kinerja yang baik. Buffer yang tidak aman memiliki kinerja yang sama atau lebih baik, terutama untuk array primitif, jika ketidakcocokan lintas platformnya dapat diterima. ByteBufferOutput dan ByteBufferInput memberikan performa yang sedikit lebih buruk, namun hal ini mungkin dapat diterima jika tujuan akhir byte harus berupa ByteBuffer.
Pengkodean panjang variabel lebih lambat dibandingkan nilai tetap, terutama bila ada banyak data yang menggunakannya.
Pengkodean potongan menggunakan buffer perantara sehingga menambahkan satu salinan tambahan dari semua byte. Ini saja mungkin dapat diterima, namun ketika digunakan dalam serializer yang masuk kembali, serializer harus membuat OutputChunked atau InputChunked untuk setiap objek. Mengalokasikan dan mengumpulkan sampah buffer tersebut selama serialisasi dapat berdampak negatif pada kinerja.
Kryo memiliki tiga set metode untuk membaca dan menulis objek. Jika kelas konkrit dari objek tersebut tidak diketahui dan objek tersebut bisa jadi null:
kryo . writeClassAndObject ( output , object );
Object object = kryo . readClassAndObject ( input );
if ( object instanceof SomeClass ) {
// ...
}
Jika kelasnya diketahui dan objeknya bisa jadi null:
kryo . writeObjectOrNull ( output , object );
SomeClass object = kryo . readObjectOrNull ( input , SomeClass . class );
Jika kelasnya diketahui dan objeknya tidak boleh null:
kryo . writeObject ( output , object );
SomeClass object = kryo . readObject ( input , SomeClass . class );
Semua metode ini pertama-tama mencari serializer yang sesuai untuk digunakan, kemudian menggunakannya untuk membuat serialisasi atau deserialisasi objek. Serializer dapat memanggil metode ini untuk serialisasi rekursif. Beberapa referensi ke objek yang sama dan referensi melingkar ditangani oleh Kryo secara otomatis.
Selain metode untuk membaca dan menulis objek, kelas Kryo menyediakan cara untuk mendaftarkan serializer, membaca dan menulis pengidentifikasi kelas secara efisien, menangani objek null untuk serializer yang tidak dapat menerima null, dan menangani referensi objek membaca dan menulis (jika diaktifkan). Hal ini memungkinkan pembuat serial untuk fokus pada tugas serialisasinya.
Saat menguji dan menjelajahi Kryo API, akan berguna untuk menulis objek ke byte, lalu membaca byte tersebut kembali ke objek.
Kryo kryo = new Kryo ();
// Register all classes to be serialized.
kryo . register ( SomeClass . class );
SomeClass object1 = new SomeClass ();
Output output = new Output ( 1024 , - 1 );
kryo . writeObject ( output , object1 );
Input input = new Input ( output . getBuffer (), 0 , output . position ());
SomeClass object2 = kryo . readObject ( input , SomeClass . class );
Dalam contoh ini Output dimulai dengan buffer yang berkapasitas 1024 byte. Jika lebih banyak byte yang ditulis ke Output, ukuran buffer akan bertambah tanpa batas. Output tidak perlu ditutup karena belum diberi OutputStream. Input dibaca langsung dari buffer byte[]
Output.
Kryo mendukung pembuatan salinan objek yang dalam dan dangkal menggunakan penugasan langsung dari satu objek ke objek lainnya. Ini lebih efisien daripada membuat serial ke byte dan kembali ke objek.
Kryo kryo = new Kryo ();
SomeClass object = ...
SomeClass copy1 = kryo . copy ( object );
SomeClass copy2 = kryo . copyShallow ( object );
Semua serializer yang digunakan harus mendukung penyalinan. Semua serializer yang dilengkapi dengan penyalinan dukungan Kryo.
Seperti halnya serialisasi, saat menyalin, beberapa referensi ke objek yang sama dan referensi melingkar ditangani oleh Kryo secara otomatis jika referensi diaktifkan.
Jika menggunakan Kryo hanya untuk menyalin, pendaftaran dapat dinonaktifkan dengan aman.
Kryo getOriginalToCopyMap
dapat digunakan setelah grafik objek disalin untuk mendapatkan peta objek lama ke objek baru. Peta dibersihkan secara otomatis oleh Kryo reset
, jadi hanya berguna jika Kryo setAutoReset
salah.
Secara default, referensi tidak diaktifkan. Artinya jika suatu objek muncul dalam grafik objek beberapa kali, objek tersebut akan ditulis beberapa kali dan akan dideserialisasi sebagai beberapa objek yang berbeda. Ketika referensi dinonaktifkan, referensi melingkar akan menyebabkan serialisasi gagal. Referensi diaktifkan atau dinonaktifkan dengan Kryo setReferences
untuk serialisasi dan setCopyReferences
untuk menyalin.
Ketika referensi diaktifkan, variant ditulis sebelum setiap objek saat pertama kali muncul di grafik objek. Untuk kemunculan berikutnya dari kelas tersebut dalam grafik objek yang sama, hanya satu varian yang ditulis. Setelah deserialisasi, referensi objek dipulihkan, termasuk referensi melingkar. Serializer yang digunakan harus mendukung referensi dengan memanggil reference
Kryo di Serializer read
.
Mengaktifkan referensi berdampak pada kinerja karena setiap objek yang dibaca atau ditulis perlu dilacak.
Di balik layar, ReferensiResolver menangani objek pelacakan yang telah dibaca atau ditulis dan memberikan ID referensi int. Beberapa implementasi disediakan:
ReferensiResolver useReferences(Class)
dapat diganti. Ia mengembalikan boolean untuk memutuskan apakah referensi didukung untuk suatu kelas. Jika suatu kelas tidak mendukung referensi, ID referensi varint tidak ditulis sebelum objek bertipe tersebut. Jika suatu kelas tidak memerlukan referensi dan objek bertipe tersebut muncul di grafik objek berkali-kali, ukuran serial dapat dikurangi secara signifikan dengan menonaktifkan referensi untuk kelas tersebut. Penyelesai referensi default mengembalikan false untuk semua pembungkus dan enum primitif. Biasanya juga mengembalikan false untuk String dan kelas lainnya, bergantung pada grafik objek yang diserialkan.
public boolean useReferences ( Class type ) {
return ! Util . isWrapperClass ( type ) && ! Util . isEnum ( type ) && type != String . class ;
}
Resolver referensi menentukan jumlah maksimum referensi dalam satu grafik objek. Indeks array Java terbatas pada Integer.MAX_VALUE
, sehingga penyelesai referensi yang menggunakan struktur data berdasarkan array dapat menghasilkan java.lang.NegativeArraySizeException
ketika membuat serialisasi lebih dari ~2 miliar objek. Kryo menggunakan ID kelas int, sehingga jumlah maksimum referensi dalam satu grafik objek dibatasi hingga seluruh rentang bilangan positif dan negatif dalam satu int (~4 miliar).
Kryo getContext
mengembalikan peta untuk menyimpan data pengguna. Instance Kryo tersedia untuk semua serializer, sehingga data ini mudah diakses oleh semua serializer.
Kryo getGraphContext
serupa, tetapi dihapus setelah setiap grafik objek diserialisasi atau dideserialisasi. Hal ini memudahkan pengelolaan status yang hanya relevan untuk grafik objek saat ini. Misalnya, ini dapat digunakan untuk menulis beberapa data skema saat pertama kali suatu kelas ditemui dalam grafik objek. Lihat KompatibelFieldSerializer sebagai contoh.
Secara default, reset
Kryo dipanggil setelah setiap grafik objek seluruhnya diserialisasi. Tindakan ini akan menyetel ulang nama kelas yang tidak terdaftar di penyelesai kelas, referensi ke objek yang diserialisasi atau dideserialisasi sebelumnya di penyelesai referensi, dan menghapus konteks grafik. Kryo setAutoReset(false)
dapat digunakan untuk menonaktifkan panggilan reset
secara otomatis, memungkinkan status tersebut menjangkau beberapa grafik objek.
Kryo adalah framework untuk memfasilitasi serialisasi. Kerangka kerja itu sendiri tidak menerapkan skema atau peduli dengan apa atau bagaimana data ditulis atau dibaca. Serializer dapat dipasang dan membuat keputusan tentang apa yang akan dibaca dan ditulis. Banyak serializer yang disediakan untuk membaca dan menulis data dengan berbagai cara. Meskipun serializer yang disediakan dapat membaca dan menulis sebagian besar objek, serializer tersebut dapat dengan mudah diganti sebagian atau seluruhnya dengan serializer Anda sendiri.
Ketika Kryo menulis sebuah instance dari suatu objek, pertama-tama ia mungkin perlu menulis sesuatu yang mengidentifikasi kelas objek tersebut. Secara default, semua kelas yang akan dibaca atau ditulis Kryo harus didaftarkan terlebih dahulu. Registrasi menyediakan ID kelas int, serializer yang akan digunakan untuk kelas tersebut, dan instantiator objek yang digunakan untuk membuat instance kelas.
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class );
Output output = ...
SomeClass object = ...
kryo . writeObject ( output , object );
Selama deserialisasi, kelas yang terdaftar harus memiliki ID yang sama persis dengan yang mereka miliki selama serialisasi. Saat didaftarkan, suatu kelas diberi ID bilangan bulat terendah berikutnya yang tersedia, yang berarti urutan kelas yang didaftarkan adalah penting. ID kelas secara opsional dapat ditentukan secara eksplisit untuk membuat pesanan menjadi tidak penting:
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class , 9 );
kryo . register ( AnotherClass . class , 10 );
kryo . register ( YetAnotherClass . class , 11 );
ID Kelas -1 dan -2 dicadangkan. ID Kelas 0-8 digunakan secara default untuk tipe primitif dan String, meskipun ID ini dapat digunakan kembali. ID ditulis sebagai varian positif yang dioptimalkan, sehingga paling efisien jika berupa bilangan bulat positif dan kecil. ID negatif tidak diserialkan secara efisien.
Di balik layar, ClassResolver menangani pembacaan dan penulisan byte untuk mewakili suatu kelas. Implementasi default sudah cukup dalam banyak kasus, namun dapat diganti untuk menyesuaikan apa yang terjadi ketika sebuah kelas didaftarkan, apa yang terjadi ketika kelas yang tidak terdaftar ditemui selama serialisasi, dan apa yang dibaca dan ditulis untuk mewakili sebuah kelas.
Kryo dapat dikonfigurasi untuk memungkinkan serialisasi tanpa mendaftarkan kelas terlebih dahulu.
Kryo kryo = new Kryo ();
kryo . setRegistrationRequired ( false );
Output output = ...
SomeClass object = ...
kryo . writeObject ( output , object );
Penggunaan kelas terdaftar dan tidak terdaftar dapat dicampur. Kelas yang tidak terdaftar memiliki dua kelemahan utama:
Jika menggunakan Kryo hanya untuk menyalin, pendaftaran dapat dinonaktifkan dengan aman.
Ketika registrasi tidak diperlukan, Kryo setWarnUnregisteredClasses
dapat diaktifkan untuk mencatat pesan ketika kelas yang tidak terdaftar ditemukan. Ini dapat digunakan untuk dengan mudah mendapatkan daftar semua kelas yang tidak terdaftar. Kryo unregisteredClassMessage
dapat diganti untuk menyesuaikan pesan log atau mengambil tindakan lain.
Ketika sebuah kelas didaftarkan, instance serializer dapat ditentukan secara opsional. Selama deserialisasi, kelas yang terdaftar harus memiliki konfigurasi serializer dan serializer yang sama persis dengan yang mereka miliki selama serialisasi.
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class , new SomeSerializer ());
kryo . register ( AnotherClass . class , new AnotherSerializer ());
Jika serializer tidak ditentukan atau ketika kelas yang tidak terdaftar ditemui, serializer dipilih secara otomatis dari daftar "serialisasi default" yang memetakan kelas ke serializer. Memiliki banyak serializer default tidak mempengaruhi performa serialisasi, jadi secara default Kryo memiliki 50+ serializer default untuk berbagai kelas JRE. Serializer default tambahan dapat ditambahkan:
Kryo kryo = new Kryo ();
kryo . setRegistrationRequired ( false );
kryo . addDefaultSerializer ( SomeClass . class , SomeSerializer . class );
Output output = ...
SomeClass object = ...
kryo . writeObject ( output , object );
Hal ini akan menyebabkan instance SomeSerializer dibuat ketika SomeClass atau kelas apa pun yang memperluas atau mengimplementasikan SomeClass didaftarkan.
Serializer default diurutkan sehingga kelas yang lebih spesifik dicocokkan terlebih dahulu, namun sebaliknya dicocokkan sesuai urutan penambahannya. Urutan penambahannya mungkin relevan untuk antarmuka.
Jika tidak ada serializer default yang cocok dengan suatu kelas, maka serializer default global akan digunakan. Serializer default global diatur ke FieldSerializer secara default, tetapi dapat diubah. Biasanya serializer global adalah salah satu yang dapat menangani berbagai tipe.
Kryo kryo = new Kryo ();
kryo . setDefaultSerializer ( TaggedFieldSerializer . class );
kryo . register ( SomeClass . class );
Dengan kode ini, dengan asumsi tidak ada serializer default yang cocok dengan SomeClass, TaggedFieldSerializer akan digunakan.
Sebuah kelas juga dapat menggunakan anotasi DefaultSerializer, yang akan digunakan alih-alih memilih salah satu serializer default Kryo:
@ DefaultSerializer ( SomeClassSerializer . class )
public class SomeClass {
// ...
}
Untuk fleksibilitas maksimum, Kryo getDefaultSerializer
dapat diganti untuk mengimplementasikan logika khusus untuk memilih dan membuat instance serializer.
Metode addDefaultSerializer(Class, Class)
tidak mengizinkan konfigurasi serializer. Pabrik serializer dapat diatur sebagai pengganti kelas serializer, sehingga pabrik dapat membuat dan mengonfigurasi setiap instance serializer. Pabrik disediakan untuk serializer umum, seringkali dengan metode getConfig
untuk mengonfigurasi serializer yang dibuat.
Kryo kryo = new Kryo ();
TaggedFieldSerializerFactory defaultFactory = new TaggedFieldSerializerFactory ();
defaultFactory . getConfig (). setReadUnknownTagData ( true );
kryo . setDefaultSerializer ( defaultFactory );
FieldSerializerFactory someClassFactory = new FieldSerializerFactory ();
someClassFactory . getConfig (). setFieldsCanBeNull ( false );
kryo . register ( SomeClass . class , someClassFactory );
Pabrik serializer memiliki metode isSupported(Class)
yang memungkinkannya menolak menangani suatu kelas, meskipun kelas tersebut cocok dengan kelas tersebut. Hal ini memungkinkan pabrik untuk memeriksa beberapa antarmuka atau menerapkan logika lainnya.
Meskipun beberapa serializer ditujukan untuk kelas tertentu, yang lain dapat membuat serialisasi banyak kelas berbeda. Serializer dapat menggunakan Kryo newInstance(Class)
untuk membuat instance kelas mana pun. Hal ini dilakukan dengan mencari registrasi kelas, lalu menggunakan ObjectInstantiator registrasi. Instansiator dapat ditentukan pada pendaftaran.
Registration registration = kryo . register ( SomeClass . class );
registration . setInstantiator ( new ObjectInstantiator < SomeClass >() {
public SomeClass newInstance () {
return new SomeClass ( "some constructor arguments" , 1234 );
}
});
Jika pendaftaran tidak memiliki instantiator, disediakan oleh Kryo newInstantiator
. Untuk menyesuaikan cara objek dibuat, Kryo newInstantiator
dapat diganti atau InstantiatorStrategy disediakan.
Kryo menyediakan DefaultInstantiatorStrategy yang membuat objek menggunakan ReflectASM untuk memanggil konstruktor argumen nol. Jika hal tersebut tidak memungkinkan, ia menggunakan refleksi untuk memanggil konstruktor argumen nol. Jika cara tersebut juga gagal, maka ia akan melontarkan pengecualian atau mencoba InstantiatorStrategy cadangan. Refleksi menggunakan setAccessible
, sehingga konstruktor argumen nol pribadi dapat menjadi cara yang baik untuk memungkinkan Kryo membuat instance kelas tanpa memengaruhi API publik.
DefaultInstantiatorStrategy adalah cara yang disarankan untuk membuat objek dengan Kryo. Ini menjalankan konstruktor seperti yang dilakukan dengan kode Java. Alternatifnya, mekanisme ekstralinguistik juga dapat digunakan untuk membuat objek. Objenesis StdInstantiatorStrategy menggunakan API khusus JVM untuk membuat instance kelas tanpa memanggil konstruktor apa pun. Menggunakan ini berbahaya karena sebagian besar kelas mengharapkan konstruktornya dipanggil. Membuat objek dengan melewati konstruktornya dapat menyebabkan objek dalam keadaan tidak diinisialisasi atau tidak valid. Kelas harus dirancang untuk diciptakan dengan cara ini.
Kryo dapat dikonfigurasi untuk mencoba DefaultInstantiatorStrategy terlebih dahulu, lalu kembali ke StdInstantiatorStrategy jika perlu.
kryo . setInstantiatorStrategy ( new DefaultInstantiatorStrategy ( new StdInstantiatorStrategy ()));
Pilihan lainnya adalah SerializingInstantiatorStrategy, yang menggunakan mekanisme serialisasi bawaan Java untuk membuat sebuah instance. Dengan menggunakan ini, kelas harus mengimplementasikan java.io.Serializable dan konstruktor argumen nol pertama di kelas super dipanggil. Ini juga melewati konstruktor dan berbahaya karena alasan yang sama seperti StdInstantiatorStrategy.
kryo . setInstantiatorStrategy ( new DefaultInstantiatorStrategy ( new SerializingInstantiatorStrategy ()));
Alternatifnya, beberapa serializer generik menyediakan metode yang dapat diganti untuk mengkustomisasi pembuatan objek untuk tipe tertentu, alih-alih memanggil Kryo newInstance
.
kryo . register ( SomeClass . class , new FieldSerializer ( kryo , SomeClass . class ) {
protected T create ( Kryo kryo , Input input , Class <? extends T > type ) {
return new SomeClass ( "some constructor arguments" , 1234 );
}
});
Beberapa serializer menyediakan metode writeHeader
yang dapat diganti untuk menulis data yang diperlukan dalam create
pada waktu yang tepat.
static public class TreeMapSerializer extends MapSerializer < TreeMap > {
protected void writeHeader ( Kryo kryo , Output output , TreeMap map ) {
kryo . writeClassAndObject ( output , map . comparator ());
}
protected TreeMap create ( Kryo kryo , Input input , Class <? extends TreeMap > type , int size ) {
return new TreeMap (( Comparator ) kryo . readClassAndObject ( input ));
}
}
Jika serializer tidak menyediakan writeHeader
, penulisan data untuk create
dapat dilakukan di write
.
static public class SomeClassSerializer extends FieldSerializer < SomeClass > {
public SomeClassSerializer ( Kryo kryo ) {
super ( kryo , SomeClass . class );
}
public void write ( Kryo kryo , Output output , SomeClass object ) {
output . writeInt ( object . value );
}
protected SomeClass create ( Kryo kryo , Input input , Class <? extends SomeClass > type ) {
return new SomeClass ( input . readInt ());
}
}
Bahkan ketika serializer mengetahui kelas yang diharapkan untuk suatu nilai (misalnya kelas bidang), jika kelas konkrit dari nilai tersebut belum final maka serializer harus terlebih dahulu menulis ID kelas, lalu nilainya. Kelas akhir dapat dibuat serial dengan lebih efisien karena bersifat non-polimorfik.
Kryo isFinal
digunakan untuk menentukan apakah suatu kelas sudah final. Metode ini dapat ditimpa untuk mengembalikan nilai true bahkan untuk tipe yang belum final. Misalnya, jika aplikasi menggunakan ArrayList secara ekstensif tetapi tidak pernah menggunakan subkelas ArrayList, memperlakukan ArrayList sebagai final dapat memungkinkan FieldSerializer menghemat 1-2 byte per bidang ArrayList.
Kryo dapat membuat serialisasi penutupan Java 8+ yang mengimplementasikan java.io.Serializable, dengan beberapa peringatan. Penutupan yang diserialkan pada satu JVM mungkin gagal untuk dideserialisasi pada JVM yang berbeda.
Kryo isClosure
digunakan untuk menentukan apakah suatu kelas merupakan penutupan. Jika demikian, maka ClosureSerializer.Closure digunakan untuk menemukan registrasi kelas, bukan kelas penutupan. Untuk membuat serialisasi penutupan, kelas-kelas berikut harus didaftarkan: ClosureSerializer.Closure, Object[], dan Class. Selain itu, kelas penangkapan penutupan harus didaftarkan.
kryo . register ( Object []. class );
kryo . register ( Class . class );
kryo . register ( ClosureSerializer . Closure . class , new ClosureSerializer ());
kryo . register ( CapturingClass . class );
Callable < Integer > closure1 = ( Callable < Integer > & java . io . Serializable )( () -> 72363 );
Output output = new Output ( 1024 , - 1 );
kryo . writeObject ( output , closure1 );
Input input = new Input ( output . getBuffer (), 0 , output . position ());
Callable < Integer > closure2 = ( Callable < Integer >) kryo . readObject ( input , ClosureSerializer . Closure . class );
Penutupan serialisasi yang tidak mengimplementasikan Serializable dapat dilakukan dengan beberapa upaya.
Kryo mendukung streaming, jadi menggunakan kompresi atau enkripsi pada semua byte serial adalah hal yang mudah:
OutputStream outputStream = new DeflaterOutputStream ( new FileOutputStream ( "file.bin" ));
Output output = new Output ( outputStream );
Kryo kryo = new Kryo ();
kryo . writeObject ( output , object );
output . close ();
Jika diperlukan, serializer dapat digunakan untuk mengompresi atau mengenkripsi byte hanya untuk sebagian byte untuk grafik objek. Misalnya, lihat DeflateSerializer atau BlowfishSerializer. Serializer ini membungkus serializer lain untuk menyandikan dan mendekode byte.
Kelas abstrak Serializer mendefinisikan metode untuk berpindah dari objek ke byte dan byte ke objek.
public class ColorSerializer extends Serializer < Color > {
public void write ( Kryo kryo , Output output , Color color ) {
output . writeInt ( color . getRGB ());
}
public Color read ( Kryo kryo , Input input , Class <? extends Color > type ) {
return new Color ( input . readInt ());
}
}
Serializer hanya memiliki dua metode yang harus diterapkan. write
menulis objek sebagai byte ke Output. read
membuat instance objek baru dan membaca dari Input untuk mengisinya.
Ketika Kryo digunakan untuk membaca objek bersarang di Serializer read
maka reference
Kryo harus dipanggil terlebih dahulu dengan objek induk jika objek yang disarangkan dapat mereferensikan objek induk. Tidak perlu memanggil reference
Kryo jika objek yang disarangkan tidak mungkin mereferensikan objek induk, jika Kryo tidak digunakan untuk objek yang disarangkan, atau jika referensi tidak digunakan. Jika objek yang disarangkan dapat menggunakan serializer yang sama, serializer tersebut harus masuk kembali.
public SomeClass read ( Kryo kryo , Input input , Class <? extends SomeClass > type ) {
SomeClass object = new SomeClass ();
kryo . reference ( object );
// Read objects that may reference the SomeClass instance.
object . someField = kryo . readClassAndObject ( input );
return object ;
}
Serializer biasanya tidak menggunakan serializer lain secara langsung, melainkan metode baca dan tulis Kryo yang harus digunakan. Hal ini memungkinkan Kryo untuk mengatur serialisasi dan menangani fitur seperti referensi dan objek null. Terkadang serializer mengetahui serializer mana yang akan digunakan untuk objek bersarang. Dalam hal ini, ia harus menggunakan metode baca dan tulis Kryo yang menerima serializer.
Jika objeknya bisa menjadi nol:
Serializer serializer = ...
kryo . writeObjectOrNull ( output , object , serializer );
SomeClass object = kryo . readObjectOrNull ( input , SomeClass . class , serializer );
Jika objek tidak boleh null:
Serializer serializer = ...
kryo . writeObject ( output , object , serializer );
SomeClass object = kryo . readObject ( input , SomeClass . class , serializer );
Selama serialisasi Kryo getDepth
memberikan kedalaman grafik objek saat ini.
Ketika serialisasi gagal, KryoException dapat dilempar dengan informasi jejak serialisasi tentang di mana pengecualian terjadi pada grafik objek. Saat menggunakan serializer bersarang, KryoException dapat ditangkap untuk menambahkan informasi jejak serialisasi.
Object object = ...
Field [] fields = ...
for ( Field field : fields ) {
try {
// Use other serializers to serialize each field.
} catch ( KryoException ex ) {
ex . addTrace ( field . getName () + " (" + object . getClass (). getName () + ")" );
throw ex ;
} catch ( Throwable t ) {
KryoException ex = new KryoException ( t );
ex . addTrace ( field . getName () + " (" + object . getClass (). getName () + ")" );
throw ex ;
}
}
Serializer yang disediakan Kryo menggunakan tumpukan panggilan saat membuat serial objek bersarang. Kryo meminimalkan panggilan tumpukan, tetapi tumpukan meluap dapat terjadi untuk grafik objek yang sangat dalam. Ini adalah masalah umum bagi sebagian besar perpustakaan serialisasi, termasuk serialisasi Java bawaan. Ukuran tumpukan dapat ditingkatkan menggunakan -Xss
, namun perhatikan bahwa ini berlaku untuk semua thread. Ukuran tumpukan yang besar di JVM dengan banyak thread mungkin menggunakan memori dalam jumlah besar.
Kryo setMaxDepth
dapat digunakan untuk membatasi kedalaman maksimum suatu grafik objek. Hal ini dapat mencegah data berbahaya menyebabkan stack overflow.
Secara default, serializer tidak akan pernah menerima null, sebagai gantinya Kryo akan menulis byte sesuai kebutuhan untuk menunjukkan null atau bukan null. Jika serializer bisa lebih efisien dengan menangani null itu sendiri, serializer dapat memanggil Serializer setAcceptsNull(true)
. Ini juga dapat digunakan untuk menghindari penulisan null yang menunjukkan byte ketika diketahui bahwa semua instance yang akan ditangani oleh serializer tidak akan pernah bernilai null.
Kryo getGenerics
menyediakan informasi tipe generik sehingga serializer bisa lebih efisien. Ini paling sering digunakan untuk menghindari penulisan kelas ketika kelas parameter tipe sudah final.
Inferensi tipe generik diaktifkan secara default dan dapat dinonaktifkan dengan Kryo setOptimizedGenerics(false)
. Menonaktifkan pengoptimalan generik dapat meningkatkan kinerja dengan mengorbankan ukuran serial yang lebih besar.
Jika kelas memiliki parameter tipe tunggal, nextGenericClass
mengembalikan kelas parameter tipe, atau null jika tidak ada. Setelah membaca atau menulis objek bersarang, popGenericType
harus dipanggil. Lihat CollectionSerializer sebagai contoh.
public class SomeClass < T > {
public T value ;
}
public class SomeClassSerializer extends Serializer < SomeClass > {
public void write ( Kryo kryo , Output output , SomeClass object ) {
Class valueClass = kryo . getGenerics (). nextGenericClass ();
if ( valueClass != null && kryo . isFinal ( valueClass )) {
Serializer serializer = kryo . getSerializer ( valueClass );
kryo . writeObjectOrNull ( output , object . value , serializer );
} else
kryo . writeClassAndObject ( output , object . value );
kryo . getGenerics (). popGenericType ();
}
public SomeClass read ( Kryo kryo , Input input , Class <? extends SomeClass > type ) {
Class valueClass = kryo . getGenerics (). nextGenericClass ();
SomeClass object = new SomeClass ();
kryo . reference ( object );
if ( valueClass != null && kryo . isFinal ( valueClass )) {
Serializer serializer = kryo . getSerializer ( valueClass );
object . value = kryo . readObjectOrNull ( input , valueClass , serializer );
} else
object . value = kryo . readClassAndObject ( input );
kryo . getGenerics (). popGenericType ();
return object ;
}
}
Untuk kelas dengan beberapa parameter tipe, nextGenericTypes
mengembalikan array instance GenericType dan resolve
digunakan untuk mendapatkan kelas untuk setiap GenericType. Setelah membaca atau menulis objek bersarang, popGenericType
harus dipanggil. Lihat MapSerializer sebagai contoh.
public class SomeClass < K , V > {
public K key ;
public V value ;
}
public class SomeClassSerializer extends Serializer < SomeClass > {
public void write ( Kryo kryo , Output output , SomeClass object ) {
Class keyClass = null , valueClass = null ;
GenericType [] genericTypes = kryo . getGenerics (). nextGenericTypes ();
if ( genericTypes != null ) {
keyClass = genericTypes [ 0 ]. resolve ( kryo . getGenerics ());
valueClass = genericTypes [ 1 ]. resolve ( kryo . getGenerics ());
}
if ( keyClass != null && kryo . isFinal ( keyClass )) {
Serializer serializer = kryo . getSerializer ( keyClass );
kryo . writeObjectOrNull ( output , object . key , serializer );
} else
kryo . writeClassAndObject ( output , object . key );
if ( valueClass != null && kryo . isFinal ( valueClass )) {
Serializer serializer = kryo . getSerializer ( valueClass );
kryo . writeObjectOrNull ( output , object . value , serializer );
} else
kryo . writeClassAndObject ( output , object . value );
kryo . getGenerics (). popGenericType ();
}
public SomeClass read ( Kryo kryo , Input input , Class <? extends SomeClass > type ) {
Class keyClass = null , valueClass = null ;
GenericType [] genericTypes = kryo . getGenerics (). nextGenericTypes ();
if ( genericTypes != null ) {
keyClass = genericTypes [ 0 ]. resolve ( kryo . getGenerics ());
valueClass = genericTypes [ 1 ]. resolve ( kryo . getGenerics ());
}
SomeClass object = new SomeClass ();
kryo . reference ( object );
if ( keyClass != null && kryo . isFinal ( keyClass )) {
Serializer serializer = kryo . getSerializer ( keyClass );
object . key = kryo . readObjectOrNull ( input , keyClass , serializer );
} else
object . key = kryo . readClassAndObject ( input );
if ( valueClass != null && kryo . isFinal ( valueClass )) {
Serializer serializer = kryo . getSerializer ( valueClass );
object . value = kryo . readObjectOrNull ( input , valueClass , serializer );
} else
object . value = kryo . readClassAndObject ( input );
kryo . getGenerics (). popGenericType ();
return object ;
}
}
Untuk serializer yang meneruskan informasi parameter tipe untuk objek bersarang dalam grafik objek (penggunaan agak lanjut), GenericsHierarchy pertama digunakan untuk menyimpan parameter tipe untuk suatu kelas. Selama serialisasi, Generics pushTypeVariables
dipanggil sebelum tipe generik diselesaikan (jika ada). Jika >0 dikembalikan, ini harus diikuti oleh Generics popTypeVariables
. Lihat FieldSerializer sebagai contoh.
public class SomeClass < T > {
T value ;
List < T > list ;
}
public class SomeClassSerializer extends Serializer < SomeClass > {
private final GenericsHierarchy genericsHierarchy ;
public SomeClassSerializer () {
genericsHierarchy = new GenericsHierarchy ( SomeClass . class );
}
public void write ( Kryo kryo , Output output , SomeClass object ) {
Class valueClass = null ;
Generics generics = kryo . getGenerics ();
int pop = 0 ;
GenericType [] genericTypes = generics . nextGenericTypes ();
if ( genericTypes != null ) {
pop = generics . pushTypeVariables ( genericsHierarchy , genericTypes );
valueClass = genericTypes [ 0 ]. resolve ( generics );
}
if ( valueClass != null && kryo . isFinal ( valueClass )) {
Serializer serializer = kryo . getSerializer ( valueClass );
kryo . writeObjectOrNull ( output , object . value , serializer );
} else
kryo . writeClassAndObject ( output , object . value );
kryo . writeClassAndObject ( output , object . list );
if ( pop > 0 ) generics . popTypeVariables ( pop );
generics . popGenericType ();
}
public SomeClass read ( Kryo kryo , Input input , Class <? extends SomeClass > type ) {
Class valueClass = null ;
Generics generics = kryo . getGenerics ();
int pop = 0 ;
GenericType [] genericTypes = generics . nextGenericTypes ();
if ( genericTypes != null ) {
pop = generics . pushTypeVariables ( genericsHierarchy , genericTypes );
valueClass = genericTypes [ 0 ]. resolve ( generics );
}
SomeClass object = new SomeClass ();
kryo . reference ( object );
if ( valueClass != null && kryo . isFinal ( valueClass )) {
Serializer serializer = kryo . getSerializer ( valueClass );
object . value = kryo . readObjectOrNull ( input , valueClass , serializer );
} else
object . value = kryo . readClassAndObject ( input );
object . list = ( List ) kryo . readClassAndObject ( input );
if ( pop > 0 ) generics . popTypeVariables ( pop );
generics . popGenericType ();
return object ;
}
}
Daripada menggunakan serializer, suatu kelas dapat memilih untuk melakukan serialisasinya sendiri dengan mengimplementasikan KryoSerializable (mirip dengan java.io.Externalizable).
public class SomeClass implements KryoSerializable {
private int value ;
public void write ( Kryo kryo , Output output ) {
output . writeInt ( value , false );
}
public void read ( Kryo kryo , Input input ) {
value = input . readInt ( false );
}
}
Tentunya instance tersebut harus sudah dibuat sebelum read
dapat dipanggil, sehingga kelas tidak dapat mengontrol pembuatannya sendiri. Kelas KryoSerializable akan menggunakan serializer default KryoSerializableSerializer, yang menggunakan Kryo newInstance
untuk membuat instance baru. Sangat mudah untuk menulis serializer Anda sendiri untuk menyesuaikan proses, memanggil metode sebelum atau sesudah serialisasi, dll.
Serializer hanya mendukung penyalinan jika copy
ditimpa. Mirip dengan Serializer read
, metode ini berisi logika untuk membuat dan mengonfigurasi salinan. Sama seperti read
, reference
Kryo harus dipanggil sebelum Kryo digunakan untuk menyalin objek anak, jika ada objek anak yang dapat mereferensikan objek induk.
class SomeClassSerializer extends Serializer < SomeClass > {
public SomeClass copy ( Kryo kryo , SomeClass original ) {
SomeClass copy = new SomeClass ();
kryo . reference ( copy );
copy . intValue = original . intValue ;
copy . object = kryo . copy ( original . object );
return copy ;
}
}
Daripada menggunakan serializer, kelas dapat mengimplementasikan KryoCopyable untuk melakukan penyalinannya sendiri:
public class SomeClass implements KryoCopyable < SomeClass > {
public SomeClass copy ( Kryo kryo ) {
SomeClass copy = new SomeClass ();
kryo . reference ( copy );
copy . intValue = intValue ;
copy . object = kryo . copy ( object );
return copy ;
}
}
Serializer setImmutable(true)
dapat digunakan ketika tipenya tidak dapat diubah. Dalam hal ini, copy
Serializer tidak perlu diimplementasikan -- implementasi copy
default akan mengembalikan objek aslinya.
Aturan praktis berikut diterapkan pada penomoran versi Kryo:
Meningkatkan ketergantungan apa pun adalah peristiwa yang signifikan, tetapi perpustakaan serialisasi lebih rentan terhadap kerusakan daripada kebanyakan dependensi. Saat meningkatkan Kryo, periksa perbedaan versi dan uji versi baru secara menyeluruh di aplikasi Anda sendiri. Kami mencoba membuatnya aman dan semudah mungkin.
Serializer Kryo yang disediakan secara default mengasumsikan bahwa Java akan digunakan untuk deserialisasi, sehingga mereka tidak secara eksplisit mendefinisikan format yang ditulis. Serializer dapat ditulis menggunakan format standar yang lebih mudah dibaca oleh bahasa lain, tetapi ini tidak disediakan secara default.
Untuk beberapa kebutuhan, seperti penyimpanan jangka panjang byte serial, bisa menjadi penting bagaimana serialisasi menangani perubahan kelas. Ini dikenal sebagai kompatibilitas ke depan (membaca byte diserialisasi oleh kelas yang lebih baru) dan kompatibilitas mundur (membaca byte yang diserialisasi oleh kelas yang lebih lama). Kryo menyediakan beberapa serial generik yang mengambil pendekatan berbeda untuk menangani kompatibilitas. Serializer tambahan dapat dengan mudah dikembangkan untuk kompatibilitas ke depan dan ke belakang, seperti serializer yang menggunakan skema tertulis tangan eksternal.
Ketika kelas berubah lebih dari serializernya dapat menangani, serializer dapat ditulis untuk mentransfer data ke kelas yang berbeda. Semua penggunaan kelas lama dalam kode aplikasi harus diganti oleh kelas baru. Kelas lama disimpan semata -mata untuk serializer ini.
kryo . register ( OldClass . class , new TaggedFieldSerializer ( kryo , OldClass . class ) {
public Object read ( Kryo kryo , Input input , Class type ) {
OldClass oldObject = ( OldClass ) super . read ( kryo , input , OldClass . class );
NewClass newObject = new NewClass ();
// Use data from the old class to populate the instance of the new class and return it.
return newObject ;
}
});
kryo . register ( NewClass . class );
Kryo menyediakan banyak serialzer dengan berbagai opsi konfigurasi dan tingkat kompatibilitas. Serializer tambahan dapat ditemukan dalam proyek saudara Kryo-Serializers, yang menjadi tuan rumah serialzer yang mengakses API pribadi atau tidak benar-benar aman di semua JVM. Lebih banyak serializer dapat ditemukan di bagian tautan.
Fieldserializer bekerja dengan membuat serial setiap bidang yang tidak benar. Ini dapat membuat serial POJOS dan banyak kelas lainnya tanpa konfigurasi. Semua bidang non-publik ditulis dan dibaca secara default, sehingga penting untuk mengevaluasi setiap kelas yang akan diserialisasi. Jika bidangnya publik, serialisasi mungkin lebih cepat.
Fieldserializer efisien dengan hanya menulis data lapangan, tanpa informasi skema, menggunakan file kelas Java sebagai skema. Itu tidak mendukung penambahan, menghapus, atau mengubah jenis bidang tanpa membatalkan byte yang sebelumnya serial. Renaming bidang diizinkan hanya jika tidak mengubah urutan alfabet dari bidang.
Kelemahan kompatibilitas Fieldserializer dapat diterima dalam banyak situasi, seperti ketika mengirim data melalui jaringan, tetapi mungkin bukan pilihan yang baik untuk penyimpanan data jangka panjang karena kelas Java tidak dapat berkembang. Dalam banyak kasus TaggedFieldSerializer adalah pilihan yang lebih baik.
Pengaturan | Keterangan | Nilai bawaan |
---|---|---|
fieldsCanBeNull | Ketika false diasumsikan bahwa tidak ada nilai bidang yang nol, yang dapat menghemat 0-1 byte per bidang. | BENAR |
setFieldsAsAccessible | Ketika benar, semua bidang yang tidak lemah (termasuk bidang pribadi) akan diserialisasi dan setAccessible jika perlu. Jika salah, hanya bidang di API publik yang akan diserialisasi. | BENAR |
ignoreSyntheticFields | Jika benar, bidang sintetis (dihasilkan oleh kompiler untuk pelingkupan) diserialisasi. | PALSU |
fixedFieldTypes | Jika benar, diasumsikan tipe beton nilai setiap bidang cocok dengan tipe bidang. Ini menghilangkan kebutuhan untuk menulis ID kelas untuk nilai -nilai bidang. | PALSU |
copyTransient | Jika benar, semua bidang sementara akan disalin. | BENAR |
serializeTransient | Jika benar, bidang sementara akan diserialisasi. | PALSU |
variableLengthEncoding | Jika benar, nilai panjang variabel digunakan untuk bidang int dan panjang. | BENAR |
extendedFieldNames | Jika benar, nama lapangan diawali dengan kelas menyatakan. Ini dapat menghindari konflik ketika subclass memiliki bidang dengan nama yang sama dengan kelas super. | PALSU |
Fieldserializer menyediakan bidang yang akan diserialisasi. Fields dapat dihapus, sehingga tidak akan diserialisasi. Bidang dapat dikonfigurasi untuk membuat serialiasi lebih efisien.
FieldSerializer fieldSerializer = ...
fieldSerializer . removeField ( "id" ); // Won't be serialized.
CachedField nameField = fieldSerializer . getField ( "name" );
nameField . setCanBeNull ( false );
CachedField someClassField = fieldSerializer . getField ( "someClass" );
someClassField . setClass ( SomeClass . class , new SomeClassSerializer ());
Pengaturan | Keterangan | Nilai bawaan |
---|---|---|
canBeNull | Ketika false diasumsikan nilai bidang tidak pernah nol, yang dapat menghemat 0-1 byte. | BENAR |
valueClass | Mengatur kelas beton dan serializer untuk digunakan untuk nilai bidang. Ini menghilangkan kebutuhan untuk menulis ID kelas untuk nilainya. Jika kelas nilai bidang adalah pembungkus primitif, primitif, atau final, pengaturan ini default ke kelas lapangan. | batal |
serializer | Mengatur serializer untuk digunakan untuk nilai bidang. Jika serializer diatur, beberapa serial membutuhkan kelas nilai untuk juga ditetapkan. Jika null, serializer terdaftar dengan Kryo untuk kelas nilai lapangan akan digunakan. | batal |
variableLengthEncoding | Jika benar, nilai panjang variabel digunakan. Ini hanya berlaku untuk bidang int atau panjang. | BENAR |
optimizePositive | Jika benar, nilai positif dioptimalkan untuk nilai panjang variabel. Ini hanya berlaku untuk bidang int atau panjang ketika pengkodean panjang variabel digunakan. | BENAR |
Anotasi dapat digunakan untuk mengonfigurasi serialzer untuk setiap bidang.
Anotasi | Keterangan |
---|---|
@Bind | Menetapkan pengaturan Cachedfield untuk bidang apa pun. |
@CollectionBind | Mengatur Pengaturan CollectionSerializer untuk bidang pengumpulan. |
@MapBind | Mengatur pengaturan mapserializer untuk bidang peta. |
@NotNull | Menandai ladang sebagai tidak pernah menjadi nol. |
public class SomeClass {
@ NotNull
@ Bind ( serializer = StringSerializer . class , valueClass = String . class , canBeNull = false )
Object stringField ;
@ Bind ( variableLengthEncoding = false )
int intField ;
@ BindMap (
keySerializer = StringSerializer . class ,
valueSerializer = IntArraySerializer . class ,
keyClass = String . class ,
valueClass = int []. class ,
keysCanBeNull = false )
Map map ;
@ BindCollection (
elementSerializer = LongArraySerializer . class ,
elementClass = long []. class ,
elementsCanBeNull = false )
Collection collection ;
}
VersionFieldSerializer memperluas fieldserializer dan memberikan kompatibilitas ke belakang. Ini berarti bidang dapat ditambahkan tanpa membatalkan byte yang sebelumnya serial. Menghapus, mengganti nama, atau mengubah jenis bidang tidak didukung.
Ketika sebuah bidang ditambahkan, ia harus memiliki anotasi @Since(int)
untuk menunjukkan versi yang ditambahkan agar kompatibel dengan byte yang sebelumnya serial. Nilai anotasi tidak boleh berubah.
VersionFieldSerializer menambahkan sangat sedikit overhead ke fieldserializer: varint tambahan tunggal.
Pengaturan | Keterangan | Nilai bawaan |
---|---|---|
compatible | Saat salah, pengecualian dilemparkan saat membaca objek dengan versi yang berbeda. Versi objek adalah versi maksimum dari bidang apa pun. | BENAR |
VersionFieldSerializer juga mewarisi semua pengaturan Fieldserializer.
TaggedFieldSerializer memperluas fieldserializer untuk memberikan kompatibilitas ke belakang dan kompatibilitas maju opsional. Ini berarti bidang dapat ditambahkan atau diganti namanya dan secara opsional dihapus tanpa membatalkan byte yang sebelumnya serial. Mengubah jenis bidang tidak didukung.
Hanya bidang yang memiliki anotasi @Tag(int)
yang diserialisasi. Nilai tag lapangan harus unik, baik di dalam kelas maupun semua kelas supernya. Pengecualian dilemparkan jika nilai tag duplikat ditemui.
Kinerja kompatibilitas dan serialisasi ke depan dan ke belakang tergantung pada pengaturan readUnknownTagData
dan pengaturan chunkedEncoding
. Selain itu, varint ditulis sebelum setiap bidang untuk nilai tag.
Ketika readUnknownTagData
dan chunkedEncoding
salah, bidang tidak boleh dihapus tetapi anotasi @Deprecated
dapat diterapkan. Bidang yang sudah usang dibaca saat membaca byte lama tetapi tidak ditulis untuk byte baru. Kelas dapat berkembang dengan membaca nilai -nilai bidang yang sudah usang dan menulisnya di tempat lain. Bidang dapat diganti namanya dan/atau dibuat pribadi untuk mengurangi kekacauan di kelas (misalnya, ignored1
, ignored2
).
Taggedfieldserializer (dengan readUnknownTagData
dan chunkedEncoding
false) adalah serializer yang disarankan untuk sebagian besar kelas di mana bidang dapat dijelaskan. Ini memungkinkan kelas untuk berkembang dan bidang untuk dihapus dari data serial (melalui depresi), memenuhi kebutuhan sebagian besar aplikasi tanpa menambahkan banyak ke ukuran serial.
Pengaturan | Keterangan | Nilai bawaan |
---|---|---|
readUnknownTagData | Ketika tag palsu dan tidak diketahui ditemui, pengecualian dilemparkan atau, jika chunkedEncoding benar, data dilewati.Saat benar, kelas untuk setiap nilai bidang ditulis sebelum nilai. Ketika tag yang tidak diketahui ditemui, upaya membaca data dibuat. Ini digunakan untuk melewatkan data dan, jika referensi diaktifkan, nilai -nilai lain dalam grafik objek merujuk bahwa data masih dapat diuraikan. Jika membaca data gagal (misalnya kelas tidak diketahui atau telah dihapus) maka pengecualian dilemparkan atau, jika chunkedEncoding benar, data dilewati.Dalam kedua kasus tersebut, jika data dilewati dan referensi diaktifkan, maka setiap referensi dalam data yang dilewati tidak dibaca dan deserialisasi lebih lanjut dapat menerima referensi yang salah dan gagal. | PALSU |
chunkedEncoding | Ketika benar, bidang ditulis dengan pengkodean yang dipotong untuk memungkinkan data lapangan yang tidak diketahui dilewati. Ini berdampak pada kinerja. | PALSU |
chunkSize | Ukuran maksimum masing -masing potongan untuk pengkodean terpotong. | 1024 |
Taggedfieldserializer juga mewarisi semua pengaturan fieldserializer.
CompatibleFieldSerializer memperluas fieldserializer untuk memberikan kompatibilitas maju dan mundur. Ini berarti bidang dapat ditambahkan atau dihapus tanpa membatalkan byte yang sebelumnya serial. Mengganti nama atau mengubah jenis bidang tidak didukung. Seperti fieldserializer, ia dapat membuat serialisasi sebagian besar kelas tanpa perlu anotasi.
Kinerja kompatibilitas dan serialisasi ke depan dan ke belakang tergantung pada pengaturan readUnknownFieldData
dan chunkedEncoding
. Selain itu, pertama kali kelas ditemui dalam byte serial, skema sederhana ditulis berisi string nama lapangan. Karena data lapangan diidentifikasi dengan nama, jika kelas super memiliki bidang dengan nama yang sama dengan subkelas, extendedFieldNames
harus benar.
Pengaturan | Keterangan | Nilai bawaan |
---|---|---|
readUnknownFieldData | Ketika bidang palsu dan tidak diketahui ditemui, pengecualian dilemparkan atau, jika chunkedEncoding benar, data dilewati.Saat benar, kelas untuk setiap nilai bidang ditulis sebelum nilai. Ketika bidang yang tidak diketahui ditemui, upaya untuk membaca data dibuat. Ini digunakan untuk melewatkan data dan, jika referensi diaktifkan, nilai -nilai lain dalam grafik objek merujuk bahwa data masih dapat diuraikan. Jika membaca data gagal (misalnya kelas tidak diketahui atau telah dihapus) maka pengecualian dilemparkan atau, jika chunkedEncoding benar, data dilewati.Dalam kedua kasus tersebut, jika data dilewati dan referensi diaktifkan, maka setiap referensi dalam data yang dilewati tidak dibaca dan deserialisasi lebih lanjut dapat menerima referensi yang salah dan gagal. | BENAR |
chunkedEncoding | Ketika benar, bidang ditulis dengan pengkodean yang dipotong untuk memungkinkan data lapangan yang tidak diketahui dilewati. Ini berdampak pada kinerja. | PALSU |
chunkSize | Ukuran maksimum masing -masing potongan untuk pengkodean terpotong. | 1024 |
CompatibleFieldserializer juga mewarisi semua pengaturan fieldserializer.
Beanserializer sangat mirip dengan Fieldserializer, kecuali menggunakan metode Bean Getter dan Setter daripada akses lapangan langsung. Ini sedikit lebih lambat, tetapi mungkin lebih aman karena menggunakan API publik untuk mengonfigurasi objek. Seperti Fieldserializer, tidak memberikan kompatibilitas maju atau mundur.
CollectionSerializer serial objek yang mengimplementasikan antarmuka java.util.collection.
Pengaturan | Keterangan | Nilai bawaan |
---|---|---|
elementsCanBeNull | Ketika false diasumsikan bahwa tidak ada elemen dalam koleksi yang nol, yang dapat menghemat 0-1 byte per elemen. | BENAR |
elementClass | Mengatur kelas beton untuk digunakan untuk setiap elemen dalam koleksi. Ini menghilangkan kebutuhan untuk menulis ID kelas untuk setiap elemen. Jika kelas elemen diketahui (misalnya melalui obat generik) dan pembungkus primitif, primitif, atau final, maka CollectionSerializer tidak akan menulis ID kelas bahkan ketika pengaturan ini nol. | batal |
elementSerializer | Mengatur serializer untuk digunakan untuk setiap elemen dalam koleksi. Jika serializer diatur, beberapa serial membutuhkan kelas nilai untuk juga ditetapkan. Jika null, serializer terdaftar dengan Kryo untuk setiap kelas elemen akan digunakan. | batal |
Mapserializer membuat serial objek yang mengimplementasikan antarmuka java.util.map.
Pengaturan | Keterangan | Nilai bawaan |
---|---|---|
keysCanBeNull | Ketika false diasumsikan bahwa tidak ada tombol di peta yang nol, yang dapat menghemat 0-1 byte per entri. | BENAR |
valuesCanBeNull | Ketika false diasumsikan bahwa tidak ada nilai dalam peta yang nol, yang dapat menghemat 0-1 byte per entri. | BENAR |
keyClass | Mengatur kelas konkret untuk digunakan untuk setiap kunci di peta. Ini menghilangkan kebutuhan untuk menulis ID kelas untuk setiap kunci. | batal |
valueClass | Mengatur kelas konkret untuk digunakan untuk setiap nilai di peta. Ini menghilangkan kebutuhan untuk menulis ID kelas untuk setiap nilai. | batal |
keySerializer | Mengatur serializer untuk digunakan untuk setiap kunci di peta. Jika serializer nilai ditetapkan, beberapa serializer mengharuskan kelas nilai juga ditetapkan. Jika null, serializer terdaftar dengan Kryo untuk setiap kelas kunci akan digunakan. | batal |
valueSerializer | Mengatur serializer untuk digunakan untuk setiap nilai di peta. Jika serializer utama diatur, beberapa serial membutuhkan kelas nilai untuk juga ditetapkan. Jika null, serializer terdaftar dengan Kryo untuk setiap kelas nilai akan digunakan. | batal |
Javaserializer dan Externalizableserializer adalah serializer Kryo yang menggunakan serialisasi bawaan Java. Ini selambat serialisasi Java biasa, tetapi mungkin diperlukan untuk kelas warisan.
java.io.externalizable dan java.io.serializable tidak memiliki serializer default yang ditetapkan secara default, sehingga serializer default harus diatur secara manual atau serial yang ditetapkan ketika kelas terdaftar.
class SomeClass implements Externalizable { /* ... */ }
kryo . addDefaultSerializer ( Externalizable . class , ExternalizableSerializer . class );
kryo . register ( SomeClass . class );
kryo . register ( SomeClass . class , new JavaSerializer ());
kryo . register ( SomeClass . class , new ExternalizableSerializer ());
Kryo memanfaatkan overhead rendah, Minlog Logging Library yang ringan. Level logging dapat ditetapkan dengan salah satu metode berikut:
Log . ERROR ();
Log . WARN ();
Log . INFO ();
Log . DEBUG ();
Log . TRACE ();
Kryo tidak mencatat INFO
(level default) dan di atas. DEBUG
nyaman untuk digunakan selama pengembangan. TRACE
baik untuk digunakan saat men -debug masalah tertentu, tetapi umumnya menghasilkan terlalu banyak informasi untuk disisihkan.
Minlog mendukung tingkat logging tetap, yang menyebabkan kompiler Java menghapus pernyataan logging di bawah level itu pada waktu kompilasi. Kryo harus dikompilasi dengan stoples minlog tingkat logging tetap.
Kryo tidak aman. Setiap utas harus memiliki instance Kryo, Input, dan Output sendiri.
Karena Kryo tidak aman dan membangun serta mengkonfigurasi instance Kryo relatif mahal, dalam lingkungan multithreaded threadlocal atau pooling mungkin dipertimbangkan.
static private final ThreadLocal < Kryo > kryos = new ThreadLocal < Kryo >() {
protected Kryo initialValue () {
Kryo kryo = new Kryo ();
// Configure the Kryo instance.
return kryo ;
};
};
Kryo kryo = kryos . get ();
Untuk pooling, Kryo menyediakan kelas biliar yang dapat mengumpulkan Kryo, input, output, atau contoh kelas lainnya.
// Pool constructor arguments: thread safe, soft references, maximum capacity
Pool < Kryo > kryoPool = new Pool < Kryo >( true , false , 8 ) {
protected Kryo create () {
Kryo kryo = new Kryo ();
// Configure the Kryo instance.
return kryo ;
}
};
Kryo kryo = kryoPool . obtain ();
// Use the Kryo instance here.
kryoPool . free ( kryo );
Pool < Output > outputPool = new Pool < Output >( true , false , 16 ) {
protected Output create () {
return new Output ( 1024 , - 1 );
}
};
Output output = outputPool . obtain ();
// Use the Output instance here.
outputPool . free ( output );
Pool < Input > inputPool = new Pool < Input >( true , false , 16 ) {
protected Input create () {
return new Input ( 1024 );
}
};
Input input = inputPool . obtain ();
// Use the Input instance here.
inputPool . free ( input );
Jika true
dilewatkan sebagai argumen pertama ke konstruktor kumpulan, kumpulan menggunakan sinkronisasi secara internal dan dapat diakses oleh beberapa utas secara bersamaan.
Jika true
dilewatkan sebagai argumen kedua ke konstruktor kolam renang, Pool menyimpan objek menggunakan java.lang.ref.softreference. Ini memungkinkan benda -benda di kolam renang dikumpulkan ketika tekanan memori pada JVM tinggi. Pool clean
menghilangkan semua referensi lunak yang objeknya telah dikumpulkan. Ini dapat mengurangi ukuran kolam ketika tidak ada kapasitas maksimum yang ditetapkan. Ketika kolam memiliki kapasitas maksimum, tidak perlu menelepon clean
karena free
Pool akan mencoba menghilangkan referensi kosong jika kapasitas maksimum telah tercapai.
Parameter pool ketiga adalah kapasitas maksimum. Jika suatu objek dibebaskan dan kumpulan sudah berisi jumlah maksimum objek gratis, objek yang ditentukan diatur ulang tetapi tidak ditambahkan ke kumpulan. Kapasitas maksimum dapat dihilangkan tanpa batas.
Jika suatu objek mengimplementasikan reset
. Ini memberi objek kesempatan untuk mengatur ulang keadaannya untuk digunakan kembali di masa depan. Atau, reset
biliar dapat ditimpa untuk mengatur ulang objek. Input dan Output mengimplementasikan poolable untuk mengatur position
dan total
ke 0. Kryo tidak mengimplementasikan poolable karena status grafik objeknya biasanya diatur ulang secara otomatis setelah setiap serialisasi (lihat Reset). Jika Anda menonaktifkan reset otomatis melalui setAutoReset(false)
, pastikan Anda menghubungi Kryo.reset()
sebelum mengembalikan instance ke kumpulan.
Pool getFree
Mengembalikan jumlah objek yang tersedia untuk diperoleh. Jika menggunakan referensi lunak, nomor ini mungkin termasuk objek yang telah dikumpulkan. clean
dapat digunakan terlebih dahulu untuk menghilangkan referensi lunak yang kosong.
Pool getPeak
Mengembalikan jumlah objek gratis tertinggi sepanjang masa. Ini dapat membantu menentukan apakah kapasitas maksimum kumpulan ditetapkan dengan tepat. Ini dapat diatur ulang kapan saja dengan resetPeak
.
Kryo menyediakan sejumlah tolok ukur berbasis JMH dan file R/GGPLOT2.
Kryo dapat dibandingkan dengan banyak perpustakaan serialisasi lainnya dalam proyek Serializer JVM. Tolok ukurnya kecil, tertanggal, dan buatan sendiri daripada menggunakan JMH, jadi kurang dapat dipercaya. Juga, sangat sulit untuk membandingkan perpustakaan serialisasi secara menyeluruh menggunakan tolok ukur. Perpustakaan memiliki banyak fitur yang berbeda dan seringkali memiliki tujuan yang berbeda, sehingga mereka dapat unggul dalam menyelesaikan masalah yang sama sekali berbeda. Untuk memahami tolok ukur ini, kode yang sedang dijalankan dan data yang sedang diserialisasi harus dianalisis dan dikontraskan dengan kebutuhan spesifik Anda. Beberapa serial sangat dioptimalkan dan menggunakan halaman kode, yang lain hanya menggunakan beberapa baris. Ini bagus untuk menunjukkan apa yang mungkin, tetapi mungkin bukan perbandingan yang relevan untuk banyak situasi.
Ada sejumlah proyek menggunakan Kryo. Beberapa tercantum di bawah ini. Harap kirimkan permintaan tarik jika Anda ingin proyek Anda disertakan di sini.