Kryo เป็นเฟรมเวิร์กการทำให้เป็นอนุกรมกราฟวัตถุไบนารีที่รวดเร็วและมีประสิทธิภาพสำหรับ Java เป้าหมายของโปรเจ็กต์คือความเร็วสูง ขนาดต่ำ และ API ที่ใช้งานง่าย โปรเจ็กต์นี้มีประโยชน์ทุกครั้งที่ต้องคงวัตถุไว้ ไม่ว่าจะเป็นไฟล์ ฐานข้อมูล หรือบนเครือข่าย
Kryo ยังสามารถทำการคัดลอก/โคลนทั้งแบบลึกและตื้นโดยอัตโนมัติ นี่เป็นการคัดลอกโดยตรงจากวัตถุหนึ่งไปยังอีกวัตถุ ไม่ใช่วัตถุหนึ่งไปยังอีกไบต์หนึ่งไปยังอีกวัตถุหนึ่ง
เอกสารนี้ใช้สำหรับ Kryo เวอร์ชัน 5.x ดู Wiki สำหรับเวอร์ชัน 4.x
โปรดใช้รายชื่ออีเมลของ Kryo สำหรับคำถาม การสนทนา และการสนับสนุน โปรดจำกัดการใช้ตัวติดตามปัญหา Kryo เฉพาะข้อบกพร่องและการปรับปรุง ไม่ใช่คำถาม การสนทนา หรือการสนับสนุน
Kryo เผยแพร่สิ่งประดิษฐ์/ขวดสองประเภท:
Kryo JARs มีอยู่ในหน้าเผยแพร่และที่ Maven Central สแน็ปช็อตล่าสุดของ Kryo รวมถึงสแนปช็อตบิลด์ของต้นแบบ อยู่ใน Sonatype Repository
หากต้องการใช้ Kryo รุ่นล่าสุดในแอปพลิเคชันของคุณ ให้ใช้รายการอ้างอิงนี้ใน pom.xml
ของคุณ :
< dependency >
< groupId >com.esotericsoftware</ groupId >
< artifactId >kryo</ artifactId >
< version >5.6.2</ version >
</ dependency >
หากต้องการใช้ Kryo รุ่นล่าสุดในไลบรารีที่คุณต้องการเผยแพร่ ให้ใช้รายการอ้างอิงนี้ใน pom.xml
ของคุณ :
< dependency >
< groupId >com.esotericsoftware.kryo</ groupId >
< artifactId >kryo5</ artifactId >
< version >5.6.2</ version >
</ dependency >
หากต้องการใช้สแน็ปช็อต Kryo ล่าสุด ให้ใช้:
< 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 >
ไม่ใช่ทุกคนที่เป็นแฟน Maven การใช้ Kryo โดยไม่มี Maven จำเป็นต้องวาง Kryo JAR บนคลาสพาธของคุณพร้อมกับ JAR ที่ขึ้นต่อกันที่พบใน lib
การสร้าง Kryo จากแหล่งที่มาต้องใช้ JDK11+ และ Maven หากต้องการสร้างสิ่งประดิษฐ์ทั้งหมด ให้รัน:
mvn clean && mvn install
ก้าวไปข้างหน้าเพื่อแสดงวิธีการใช้ห้องสมุด:
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 ;
}
}
คลาส Kryo ดำเนินการซีเรียลไลซ์โดยอัตโนมัติ คลาสเอาท์พุตและอินพุตจัดการบัฟเฟอร์ไบต์และสามารถเลือกฟลัชไปยังสตรีมได้
เอกสารส่วนที่เหลือนี้มีรายละเอียดวิธีการทำงานและการใช้งานห้องสมุดขั้นสูง
การรับข้อมูลเข้าและออกจาก Kryo ทำได้โดยใช้คลาส Input และ Output คลาสเหล่านี้ไม่ปลอดภัยสำหรับเธรด
คลาส Output คือ OutputStream ที่เขียนข้อมูลไปยังบัฟเฟอร์อาร์เรย์ไบต์ สามารถรับบัฟเฟอร์นี้และใช้งานได้โดยตรง หากต้องการอาร์เรย์ไบต์ หากเอาต์พุตได้รับ OutputStream มันจะล้างไบต์ไปยังสตรีมเมื่อบัฟเฟอร์เต็ม มิฉะนั้นเอาต์พุตสามารถเพิ่มบัฟเฟอร์ได้โดยอัตโนมัติ เอาต์พุตมีวิธีการมากมายในการเขียนดั้งเดิมและสตริงเป็นไบต์อย่างมีประสิทธิภาพ มีฟังก์ชันการทำงานคล้ายกับ DataOutputStream, BufferedOutputStream, FilterOutputStream และ ByteArrayOutputStream ทั้งหมดในคลาสเดียว
เคล็ดลับ: เอาต์พุตและอินพุตมีฟังก์ชันการทำงานของ ByteArrayOutputStream ทั้งหมด ไม่มีเหตุผลใดที่จะต้องล้างเอาต์พุตไปยัง ByteArrayOutputStream
เอาต์พุตบัฟเฟอร์ไบต์เมื่อเขียนไปยัง OutputStream ดังนั้นจะต้องเรียก flush
หรือ close
หลังจากการเขียนเสร็จสมบูรณ์ เพื่อทำให้ไบต์ที่บัฟเฟอร์ถูกเขียนไปยัง OutputStream หากเอาท์พุตไม่ได้รับการจัดเตรียม OutputStream การเรียก flush
หรือ close
ก็ไม่จำเป็น อินสแตนซ์เอาท์พุตต่างจากสตรีมอื่นๆ ตรงที่สามารถนำกลับมาใช้ซ้ำได้โดยการตั้งค่าตำแหน่ง หรือการตั้งค่าอาร์เรย์ไบต์หรือสตรีมใหม่
เคล็ดลับ: เนื่องจากบัฟเฟอร์เอาต์พุตอยู่แล้ว จึงไม่มีเหตุผลที่จะต้องล้างเอาต์พุตไปที่ BufferedOutputStream
อาร์กิวเมนต์เอาท์พุตคอนสตรัคเตอร์เป็นศูนย์สร้างเอาท์พุตที่ไม่ได้กำหนดค่าเริ่มต้น ต้องเรียกเอาต์พุต setBuffer
ก่อนจึงจะสามารถใช้เอาต์พุตได้
คลาสอินพุตเป็น InputStream ที่อ่านข้อมูลจากบัฟเฟอร์อาร์เรย์ไบต์ บัฟเฟอร์นี้สามารถตั้งค่าได้โดยตรง หากต้องการอ่านจากอาร์เรย์ไบต์ หากอินพุตได้รับ InputStream มันจะเติมบัฟเฟอร์จากสตรีมเมื่อมีการอ่านข้อมูลทั้งหมดในบัฟเฟอร์แล้ว อินพุตมีวิธีการมากมายในการอ่านค่าดั้งเดิมและสตริงจากไบต์อย่างมีประสิทธิภาพ มีฟังก์ชันการทำงานคล้ายกับ DataInputStream, BufferedInputStream, FilterInputStream และ ByteArrayInputStream ทั้งหมดในคลาสเดียว
เคล็ดลับ: อินพุตมีฟังก์ชันทั้งหมดของ ByteArrayInputStream ไม่มีเหตุผลใดที่จะให้ Input อ่านจาก ByteArrayInputStream
หากมีการเรียก close
อินพุต สตรีมอินพุตของอินพุตจะถูกปิด ถ้ามี หากไม่ได้อ่านจาก InputStream ก็ไม่จำเป็นต้องเรียก close
อินสแตนซ์อินพุตต่างจากสตรีมอื่นๆ ตรงที่สามารถนำกลับมาใช้ซ้ำได้โดยการตั้งค่าตำแหน่งและขีดจำกัด หรือการตั้งค่าอาร์เรย์ไบต์ใหม่หรือ InputStream
อาร์กิวเมนต์อินพุตเป็นศูนย์สร้างอินพุตที่ไม่ได้เตรียมใช้งาน ต้องเรียกอินพุต setBuffer
ก่อนจึงจะสามารถใช้อินพุตได้
คลาส ByteBufferOutput และ ByteBufferInput ทำงานเหมือนกับ Output และ Input ทุกประการ ยกเว้นว่าจะใช้ ByteBuffer แทนที่จะเป็นอาร์เรย์ไบต์
คลาส UnsafeOutput, UnsafeInput, UnsafeByteBufferOutput และ UnsafeByteBufferInput ทำงานเหมือนกับคลาสที่ไม่ปลอดภัยทุกประการ ยกเว้นว่าจะใช้ sun.misc.Unsafe เพื่อประสิทธิภาพที่สูงขึ้นในหลายกรณี หากต้องการใช้คลาสเหล่านี้ Util.unsafe
จะต้องเป็นจริง
ข้อเสียของการใช้บัฟเฟอร์ที่ไม่ปลอดภัยก็คือ endianness ดั้งเดิมและการแสดงประเภทตัวเลขของระบบที่ดำเนินการซีเรียลไลซ์จะส่งผลต่อข้อมูลที่ซีเรียลไลซ์ ตัวอย่างเช่น การดีซีเรียลไลซ์จะล้มเหลวหากข้อมูลถูกเขียนบน X86 และอ่านบน SPARC นอกจากนี้ หากข้อมูลถูกเขียนด้วยบัฟเฟอร์ที่ไม่ปลอดภัย จะต้องอ่านข้อมูลนั้นด้วยบัฟเฟอร์ที่ไม่ปลอดภัย
ความแตกต่างด้านประสิทธิภาพที่ใหญ่ที่สุดกับบัฟเฟอร์ที่ไม่ปลอดภัยคืออาร์เรย์ดั้งเดิมขนาดใหญ่เมื่อไม่ได้ใช้การเข้ารหัสความยาวผันแปร การเข้ารหัสความยาวผันแปรสามารถปิดใช้งานได้สำหรับบัฟเฟอร์ที่ไม่ปลอดภัยหรือเฉพาะฟิลด์ที่ระบุเท่านั้น (เมื่อใช้ FieldSerializer)
คลาส IO จัดเตรียมวิธีการอ่านและเขียนค่า int (varint) และ long (varlong) ที่มีความยาวผันแปรได้ ซึ่งทำได้โดยใช้บิตที่ 8 ของแต่ละไบต์เพื่อระบุว่ามีไบต์เพิ่มขึ้นหรือไม่ ซึ่งหมายความว่า varint ใช้ 1-5 ไบต์ และ varlong ใช้ 1-9 ไบต์ การใช้การเข้ารหัสความยาวผันแปรจะมีราคาแพงกว่า แต่จะทำให้ข้อมูลซีเรียลไลซ์มีขนาดเล็กลงมาก
เมื่อเขียนค่าความยาวผันแปร ค่าสามารถปรับให้เหมาะสมสำหรับค่าบวกหรือทั้งค่าลบและค่าบวก ตัวอย่างเช่น เมื่อปรับให้เหมาะสมสำหรับค่าบวก 0 ถึง 127 จะถูกเขียนเป็นหนึ่งไบต์, 128 ถึง 16383 จะถูกเขียนเป็นสองไบต์ เป็นต้น อย่างไรก็ตาม จำนวนลบเล็กๆ จะเป็นกรณีที่แย่ที่สุดที่ 5 ไบต์ เมื่อไม่ได้รับการปรับให้เหมาะสมสำหรับค่าบวก ช่วงเหล่านี้จะถูกเลื่อนลงครึ่งหนึ่ง ตัวอย่างเช่น -64 ถึง 63 เขียนเป็นหนึ่งไบต์, 64 ถึง 8191 และ -65 ถึง -8192 เขียนเป็นสองไบต์ เป็นต้น
บัฟเฟอร์อินพุตและเอาต์พุตมีวิธีในการอ่านและเขียนค่าขนาดคงที่หรือความยาวผันแปรได้ นอกจากนี้ยังมีวิธีการที่อนุญาตให้บัฟเฟอร์ตัดสินใจว่าจะเขียนค่าขนาดคงที่หรือความยาวผันแปรหรือไม่ ซึ่งอนุญาตให้ใช้รหัสซีเรียลไลซ์เพื่อให้แน่ใจว่ามีการใช้การเข้ารหัสความยาวผันแปรสำหรับค่าทั่วไปที่จะขยายเอาต์พุตหากใช้ขนาดคงที่ ในขณะที่ยังคงอนุญาตให้การกำหนดค่าบัฟเฟอร์ตัดสินใจหาค่าอื่นๆ ทั้งหมด
วิธี | คำอธิบาย |
---|---|
เขียนInt(int) | เขียน int ขนาด 4 ไบต์ |
writeVarInt(int, บูลีน) | เขียน int 1-5 ไบต์ |
writeInt(int, บูลีน) | เขียน int 4 หรือ 1-5 ไบต์ (บัฟเฟอร์จะตัดสินใจ) |
เขียนยาว(ยาว) | เขียนความยาว 8 ไบต์ |
writeVarLong(ยาว, บูลีน) | เขียนความยาว 1-9 ไบต์ |
writeLong(ยาว, บูลีน) | เขียนความยาว 8 หรือ 1-9 ไบต์ (บัฟเฟอร์จะตัดสินใจ) |
หากต้องการปิดใช้งานการเข้ารหัสความยาวผันแปรสำหรับค่าทั้งหมด วิธีการ writeVarInt
, writeVarLong
, readVarInt
และ readVarLong
จะต้องถูกแทนที่
การเขียนความยาวของข้อมูลบางส่วนตามด้วยข้อมูลจะเป็นประโยชน์ เมื่อไม่ทราบความยาวของข้อมูลล่วงหน้า ข้อมูลทั้งหมดจะต้องได้รับการบัฟเฟอร์เพื่อกำหนดความยาวของข้อมูล จากนั้นจึงสามารถเขียนความยาวได้ จากนั้นจึงเป็นข้อมูล การใช้บัฟเฟอร์ขนาดใหญ่เพียงตัวเดียวจะป้องกันการสตรีมและอาจต้องใช้บัฟเฟอร์ขนาดใหญ่เกินสมควร ซึ่งไม่เหมาะ
การเข้ารหัสเป็นกลุ่มช่วยแก้ปัญหานี้โดยใช้บัฟเฟอร์ขนาดเล็ก เมื่อบัฟเฟอร์เต็ม ความยาวของมันจะถูกเขียน จากนั้นจึงเขียนข้อมูล นี่คือหนึ่งส่วนของข้อมูล บัฟเฟอร์ถูกล้างและจะดำเนินต่อไปจนกว่าจะไม่มีข้อมูลที่จะเขียนอีกต่อไป ชิ้นที่มีความยาวเป็นศูนย์หมายถึงจุดสิ้นสุดของชิ้น
Kryo มีคลาสที่ทำให้การเข้ารหัสแบบ chunked ใช้งานง่าย OutputChunked ใช้สำหรับเขียนข้อมูลแบบก้อน มันขยายเอาต์พุตดังนั้นจึงมีวิธีการเขียนข้อมูลที่สะดวกทั้งหมด เมื่อบัฟเฟอร์ OutputChunked เต็ม มันจะล้างก้อนข้อมูลไปยัง OutputStream อื่น วิธี endChunk
ใช้เพื่อทำเครื่องหมายจุดสิ้นสุดของชุดชิ้นส่วน
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 ();
หากต้องการอ่านข้อมูลแบบก้อน ระบบจะใช้ InputChunked มันขยายการป้อนข้อมูล ดังนั้นจึงมีวิธีการที่สะดวกในการอ่านข้อมูลทั้งหมด เมื่ออ่าน InputChunked จะปรากฏที่จุดสิ้นสุดของข้อมูลเมื่อถึงจุดสิ้นสุดของชุดข้อมูล เมธอด nextChunks
จะเลื่อนไปยังชุดถัดไปของชิ้นข้อมูล แม้ว่าจะไม่ได้อ่านข้อมูลทั้งหมดจากชุดของชิ้นปัจจุบันก็ตาม
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 ();
โดยทั่วไปแล้วเอาต์พุตและอินพุตจะให้ประสิทธิภาพที่ดี บัฟเฟอร์ที่ไม่ปลอดภัยทำงานได้ดีหรือดีกว่า โดยเฉพาะอย่างยิ่งสำหรับอาร์เรย์แบบดั้งเดิม หากความเข้ากันไม่ได้ระหว่างแพลตฟอร์มของบัฟเฟอร์นั้นเป็นที่ยอมรับได้ ByteBufferOutput และ ByteBufferInput ให้ประสิทธิภาพที่แย่ลงเล็กน้อย แต่อาจยอมรับได้หากปลายทางสุดท้ายของไบต์ต้องเป็น ByteBuffer
การเข้ารหัสความยาวผันแปรจะช้ากว่าค่าคงที่ โดยเฉพาะอย่างยิ่งเมื่อมีข้อมูลจำนวนมากใช้งาน
การเข้ารหัสแบบกลุ่มใช้บัฟเฟอร์ตัวกลางดังนั้นจึงเพิ่มอีกหนึ่งสำเนาของไบต์ทั้งหมด สิ่งนี้เพียงอย่างเดียวอาจยอมรับได้ แต่เมื่อใช้ใน reentrant serializer ตัวซีเรียลไลเซอร์จะต้องสร้าง OutputChunked หรือ InputChunked สำหรับแต่ละอ็อบเจ็กต์ การจัดสรรและการรวบรวมบัฟเฟอร์เหล่านั้นระหว่างการทำให้เป็นอนุกรมอาจส่งผลเสียต่อประสิทธิภาพการทำงาน
Kryo มีวิธีการอ่านและเขียนวัตถุสามชุด หากไม่ทราบคลาสที่เป็นรูปธรรมของวัตถุและวัตถุอาจเป็นโมฆะ:
kryo . writeClassAndObject ( output , object );
Object object = kryo . readClassAndObject ( input );
if ( object instanceof SomeClass ) {
// ...
}
ถ้าคลาสเป็นที่รู้จักและวัตถุอาจเป็นโมฆะ:
kryo . writeObjectOrNull ( output , object );
SomeClass object = kryo . readObjectOrNull ( input , SomeClass . class );
หากรู้จักคลาสและวัตถุไม่สามารถเป็นค่าว่างได้:
kryo . writeObject ( output , object );
SomeClass object = kryo . readObject ( input , SomeClass . class );
วิธีการทั้งหมดเหล่านี้ค้นหาซีเรียลไลเซอร์ที่เหมาะสมก่อน จากนั้นจึงใช้วิธีนั้นเพื่อทำให้ซีเรียลไลซ์หรือดีซีเรียลไลซ์อ็อบเจ็กต์ Serializers สามารถเรียกวิธีการเหล่านี้สำหรับซีเรียลไลซ์แบบเรียกซ้ำได้ การอ้างอิงหลายรายการไปยังวัตถุเดียวกันและการอ้างอิงแบบวงกลมได้รับการจัดการโดย Kryo โดยอัตโนมัติ
นอกจากวิธีการอ่านและเขียนอ็อบเจ็กต์แล้ว คลาส Kryo ยังจัดเตรียมวิธีการลงทะเบียนซีเรียลไลเซอร์ อ่านและเขียนตัวระบุคลาสอย่างมีประสิทธิภาพ จัดการอ็อบเจ็กต์ null สำหรับซีเรียลไลเซอร์ที่ไม่สามารถยอมรับค่า null และจัดการการอ่านและการเขียนการอ้างอิงอ็อบเจ็กต์ (หากเปิดใช้งาน) ซึ่งช่วยให้ผู้ซีเรียลไลเซอร์มุ่งเน้นไปที่งานซีเรียลไลซ์ได้
ในขณะที่ทดสอบและสำรวจ Kryo API การเขียนออบเจ็กต์เป็นไบต์จะมีประโยชน์ จากนั้นอ่านไบต์เหล่านั้นกลับไปยังออบเจ็กต์
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 );
ในตัวอย่างนี้ เอาต์พุตเริ่มต้นด้วยบัฟเฟอร์ที่มีความจุ 1,024 ไบต์ หากมีการเขียนไบต์ไปยังเอาต์พุตมากขึ้น บัฟเฟอร์จะมีขนาดเพิ่มขึ้นโดยไม่มีขีดจำกัด ไม่จำเป็นต้องปิดเอาต์พุตเนื่องจากไม่ได้รับ OutputStream อินพุตอ่านโดยตรงจากบัฟเฟอร์ byte[]
ของเอาต์พุต
Kryo รองรับการทำสำเนาวัตถุที่ลึกและตื้นโดยใช้การกำหนดโดยตรงจากวัตถุหนึ่งไปยังอีกวัตถุหนึ่ง สิ่งนี้มีประสิทธิภาพมากกว่าการทำให้เป็นอนุกรมเป็นไบต์และกลับไปยังอ็อบเจ็กต์
Kryo kryo = new Kryo ();
SomeClass object = ...
SomeClass copy1 = kryo . copy ( object );
SomeClass copy2 = kryo . copyShallow ( object );
ซีเรียลไลเซอร์ทั้งหมดที่ใช้จำเป็นต้องรองรับการคัดลอก ซีเรียลไลเซอร์ทั้งหมดที่มาพร้อมกับ Kryo รองรับการคัดลอก
เช่นเดียวกับการทำให้เป็นอนุกรม เมื่อคัดลอก การอ้างอิงหลายรายการไปยังออบเจ็กต์เดียวกันและการอ้างอิงแบบวงกลมจะได้รับการจัดการโดย Kryo โดยอัตโนมัติหากเปิดใช้งานการอ้างอิง
หากใช้ Kryo เพื่อการคัดลอกเท่านั้น การลงทะเบียนสามารถปิดใช้งานได้อย่างปลอดภัย
Kryo getOriginalToCopyMap
สามารถใช้ได้หลังจากคัดลอกกราฟวัตถุเพื่อรับแผนที่ของวัตถุเก่าไปยังวัตถุใหม่ แผนที่จะถูกล้างโดยอัตโนมัติโดย reset
Kryo ดังนั้นจึงมีประโยชน์เฉพาะเมื่อ Kryo setAutoReset
เป็นเท็จ
โดยค่าเริ่มต้น การอ้างอิงไม่ได้เปิดใช้งาน ซึ่งหมายความว่าหากวัตถุปรากฏในกราฟวัตถุหลายครั้ง วัตถุนั้นจะถูกเขียนหลายครั้ง และจะถูกดีซีเรียลไลซ์เป็นวัตถุหลายชิ้นที่แตกต่างกัน เมื่อปิดใช้งานการอ้างอิง การอ้างอิงแบบวงกลมจะทำให้การทำให้เป็นอนุกรมล้มเหลว การอ้างอิงถูกเปิดหรือปิดใช้งานด้วย Kryo setReferences
สำหรับการซีเรียลไลซ์และ setCopyReferences
สำหรับการคัดลอก
เมื่อเปิดใช้งานการอ้างอิง varint จะถูกเขียนก่อนแต่ละออบเจ็กต์ในครั้งแรกที่ปรากฏในกราฟออบเจ็กต์ สำหรับการปรากฏครั้งต่อไปของคลาสนั้นภายในกราฟวัตถุเดียวกัน จะมีการเขียนเฉพาะตัวแปรเท่านั้น หลังจากการดีซีเรียลไลซ์ การอ้างอิงอ็อบเจ็กต์จะถูกกู้คืน รวมถึงการอ้างอิงแบบวงกลมด้วย ตัวซีเรียลไลเซอร์ที่ใช้งานจะต้องรองรับการอ้างอิงโดยการเรียก Kryo reference
ใน Serializer read
การเปิดใช้งานการอ้างอิงส่งผลกระทบต่อประสิทธิภาพเนื่องจากทุกออบเจ็กต์ที่อ่านหรือเขียนจำเป็นต้องได้รับการติดตาม
ภายใต้หน้าปก ReferenceResolver จะจัดการออบเจ็กต์การติดตามที่ถูกอ่านหรือเขียน และจัดเตรียม ID อ้างอิง int มีการใช้งานหลายอย่าง:
ReferenceResolver useReferences(Class)
สามารถแทนที่ได้ มันจะส่งคืนบูลีนเพื่อตัดสินใจว่าการอ้างอิงได้รับการสนับสนุนสำหรับคลาสหรือไม่ หากคลาสไม่รองรับการอ้างอิง ID การอ้างอิง varint จะไม่ถูกเขียนก่อนอ็อบเจ็กต์ประเภทนั้น หากคลาสไม่ต้องการการอ้างอิงและออบเจ็กต์ประเภทนั้นปรากฏในกราฟออบเจ็กต์หลายครั้ง ขนาดซีเรียลไลซ์สามารถลดลงได้อย่างมากโดยการปิดใช้งานการอ้างอิงสำหรับคลาสนั้น ตัวแก้ไขการอ้างอิงเริ่มต้นส่งคืนค่าเท็จสำหรับ wrapper และ enum ดั้งเดิมทั้งหมด เป็นเรื่องปกติที่จะคืนค่าเท็จสำหรับสตริงและคลาสอื่นๆ ขึ้นอยู่กับกราฟวัตถุที่กำลังซีเรียลไลซ์
public boolean useReferences ( Class type ) {
return ! Util . isWrapperClass ( type ) && ! Util . isEnum ( type ) && type != String . class ;
}
ตัวแก้ไขการอ้างอิงจะกำหนดจำนวนการอ้างอิงสูงสุดในกราฟวัตถุเดียว ดัชนีอาร์เรย์ Java ถูกจำกัดไว้ที่ Integer.MAX_VALUE
ดังนั้นตัวแก้ไขการอ้างอิงที่ใช้โครงสร้างข้อมูลตามอาร์เรย์อาจส่งผลให้เกิด java.lang.NegativeArraySizeException
เมื่อทำให้อนุกรมอ็อบเจ็กต์มากกว่า ~2 พันล้านอ็อบเจ็กต์ Kryo ใช้รหัสคลาส int ดังนั้นจำนวนการอ้างอิงสูงสุดในกราฟวัตถุเดียวจึงถูกจำกัดไว้ที่ช่วงจำนวนบวกและลบทั้งหมดใน int (~4 พันล้าน)
Kryo getContext
ส่งคืนแผนที่สำหรับการจัดเก็บข้อมูลผู้ใช้ Kryo instance พร้อมใช้งานสำหรับซีเรียลไลเซอร์ทั้งหมด ดังนั้นซีเรียลไลเซอร์ทั้งหมดจึงเข้าถึงข้อมูลนี้ได้อย่างง่ายดาย
Kryo getGraphContext
คล้ายกัน แต่จะถูกล้างหลังจากแต่ละกราฟวัตถุถูกทำให้เป็นอนุกรมหรือดีซีเรียลไลซ์ ทำให้ง่ายต่อการจัดการสถานะที่เกี่ยวข้องกับกราฟวัตถุปัจจุบันเท่านั้น ตัวอย่างเช่น สามารถใช้เพื่อเขียนข้อมูลสคีมาบางส่วนในครั้งแรกที่พบคลาสในกราฟวัตถุ ดูตัวอย่างที่ CompatibleFieldSerializer
ตามค่าเริ่มต้น reset
Kryo จะถูกเรียกหลังจากแต่ละกราฟวัตถุทั้งหมดถูกทำให้เป็นอนุกรม วิธีนี้จะรีเซ็ตชื่อคลาสที่ไม่ได้ลงทะเบียนในตัวแก้ไขคลาส การอ้างอิงไปยังอ็อบเจ็กต์ซีเรียลไลซ์หรือดีซีเรียลไลซ์ก่อนหน้านี้ในรีโซลเวอร์อ้างอิง และล้างบริบทของกราฟ Kryo setAutoReset(false)
สามารถใช้เพื่อปิดใช้งาน reset
การโทรโดยอัตโนมัติ ทำให้สถานะนั้นขยายกราฟวัตถุหลายรายการได้
Kryo เป็นกรอบการทำงานที่อำนวยความสะดวกในการทำให้เป็นอนุกรม กรอบงานเองไม่ได้บังคับใช้สคีมาหรือสนใจว่าข้อมูลจะถูกเขียนหรืออ่านอย่างไร Serializers สามารถเสียบปลั๊กและตัดสินใจว่าจะอ่านและเขียนอะไร มีซีเรียลไลเซอร์หลายตัวเตรียมไว้ให้เพื่ออ่านและเขียนข้อมูลในรูปแบบต่างๆ แม้ว่าซีเรียลไลเซอร์ที่ให้มาจะสามารถอ่านและเขียนออบเจ็กต์ส่วนใหญ่ได้ แต่ก็สามารถเปลี่ยนบางส่วนหรือทั้งหมดด้วยซีเรียลไลเซอร์ของคุณเองได้อย่างง่ายดาย
เมื่อ Kryo เขียนอินสแตนซ์ของวัตถุ อันดับแรกมันอาจจำเป็นต้องเขียนบางสิ่งที่ระบุคลาสของวัตถุ ตามค่าเริ่มต้น คลาสทั้งหมดที่ Kryo จะอ่านหรือเขียนจะต้องลงทะเบียนไว้ล่วงหน้า การลงทะเบียนจะระบุ ID คลาส int, ซีเรียลไลเซอร์ที่จะใช้สำหรับคลาส และตัวสร้างอินสแตนซ์ของอ็อบเจ็กต์ที่ใช้ในการสร้างอินสแตนซ์ของคลาส
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class );
Output output = ...
SomeClass object = ...
kryo . writeObject ( output , object );
ในระหว่างการดีซีเรียลไลซ์ คลาสที่ลงทะเบียนจะต้องมี ID เดียวกันกับที่มีในระหว่างการซีเรียลไลซ์ เมื่อลงทะเบียน คลาสจะถูกกำหนด ID จำนวนเต็มต่ำสุดถัดไปที่มีอยู่ ซึ่งหมายความว่าคลาสลำดับที่ลงทะเบียนมีความสำคัญ ID คลาสสามารถระบุได้อย่างชัดเจนเพื่อทำให้ลำดับไม่สำคัญ:
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class , 9 );
kryo . register ( AnotherClass . class , 10 );
kryo . register ( YetAnotherClass . class , 11 );
รหัสคลาส -1 และ -2 สงวนไว้ รหัสคลาส 0-8 ถูกใช้เป็นค่าเริ่มต้นสำหรับประเภทดั้งเดิมและสตริง แม้ว่ารหัสเหล่านี้สามารถนำไปใช้ใหม่ได้ ID ถูกเขียนเป็นรูปแบบบวกที่ได้รับการปรับปรุง ดังนั้นจึงมีประสิทธิภาพมากที่สุดเมื่อเป็นจำนวนเต็มบวกขนาดเล็ก รหัสเชิงลบไม่ได้ทำให้เป็นอนุกรมอย่างมีประสิทธิภาพ
ภายใต้หน้าปก ClassResolver จะจัดการการอ่านและการเขียนไบต์จริง ๆ เพื่อเป็นตัวแทนของคลาส การใช้งานเริ่มต้นนั้นเพียงพอแล้วในกรณีส่วนใหญ่ แต่สามารถแทนที่ได้เพื่อปรับแต่งสิ่งที่เกิดขึ้นเมื่อมีการลงทะเบียนคลาส สิ่งที่จะเกิดขึ้นเมื่อพบคลาสที่ไม่ได้ลงทะเบียนระหว่างการทำให้เป็นอนุกรม และสิ่งที่อ่านและเขียนเพื่อเป็นตัวแทนของคลาส
สามารถกำหนดค่า Kryo เพื่ออนุญาตการทำให้เป็นอนุกรมโดยไม่ต้องลงทะเบียนคลาสล่วงหน้า
Kryo kryo = new Kryo ();
kryo . setRegistrationRequired ( false );
Output output = ...
SomeClass object = ...
kryo . writeObject ( output , object );
การใช้คลาสที่ลงทะเบียนและไม่ได้ลงทะเบียนสามารถผสมกันได้ ชั้นเรียนที่ไม่ได้ลงทะเบียนมีข้อเสียหลักสองประการ:
หากใช้ Kryo เพื่อการคัดลอกเท่านั้น การลงทะเบียนสามารถปิดใช้งานได้อย่างปลอดภัย
เมื่อไม่จำเป็นต้องลงทะเบียน คุณสามารถเปิดใช้งาน Kryo setWarnUnregisteredClasses
เพื่อบันทึกข้อความเมื่อพบคลาสที่ไม่ได้ลงทะเบียน สามารถใช้เพื่อรับรายการคลาสที่ไม่ได้ลงทะเบียนทั้งหมดได้อย่างง่ายดาย Kryo unregisteredClassMessage
สามารถแทนที่ได้เพื่อปรับแต่งข้อความบันทึกหรือดำเนินการอื่นๆ
เมื่อลงทะเบียนคลาสแล้ว สามารถเลือกระบุอินสแตนซ์ซีเรียลไลเซอร์ได้ ในระหว่างการดีซีเรียลไลซ์ คลาสที่ลงทะเบียนจะต้องมีการกำหนดค่าซีเรียลไลเซอร์และซีเรียลไลเซอร์เหมือนกันทุกประการที่มีระหว่างซีเรียลไลซ์
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class , new SomeSerializer ());
kryo . register ( AnotherClass . class , new AnotherSerializer ());
หากไม่ได้ระบุซีเรียลไลเซอร์หรือเมื่อพบคลาสที่ไม่ได้ลงทะเบียน ซีเรียลไลเซอร์จะถูกเลือกโดยอัตโนมัติจากรายการ "ซีเรียลไลเซอร์เริ่มต้น" ที่แมปคลาสกับซีเรียลไลเซอร์ การมีซีเรียลไลเซอร์เริ่มต้นจำนวนมากไม่ส่งผลต่อประสิทธิภาพการทำให้ซีเรียลไลซ์ ดังนั้นตามค่าเริ่มต้น Kryo จึงมีซีเรียลไลเซอร์เริ่มต้นมากกว่า 50 รายการสำหรับคลาส JRE ต่างๆ สามารถเพิ่มซีเรียลไลเซอร์เริ่มต้นเพิ่มเติมได้:
Kryo kryo = new Kryo ();
kryo . setRegistrationRequired ( false );
kryo . addDefaultSerializer ( SomeClass . class , SomeSerializer . class );
Output output = ...
SomeClass object = ...
kryo . writeObject ( output , object );
สิ่งนี้จะทำให้อินสแตนซ์ SomeSerializer ถูกสร้างขึ้นเมื่อมีการลงทะเบียน SomeClass หรือคลาสใด ๆ ที่ขยายหรือใช้งาน SomeClass
ซีเรียลไลเซอร์เริ่มต้นจะถูกจัดเรียงเพื่อให้คลาสที่เจาะจงมากขึ้นถูกจับคู่ก่อน แต่จะจับคู่ตามลำดับที่เพิ่มเข้าไป ลำดับที่เพิ่มอาจเกี่ยวข้องกับอินเทอร์เฟซ
หากไม่มีซีเรียลไลเซอร์เริ่มต้นที่ตรงกับคลาส ระบบจะใช้ซีเรียลไลเซอร์เริ่มต้นส่วนกลาง Serializer เริ่มต้นส่วนกลางถูกตั้งค่าเป็น FieldSerializer ตามค่าเริ่มต้น แต่สามารถเปลี่ยนแปลงได้ โดยปกติแล้ว global serializer จะเป็นตัวที่สามารถรองรับได้หลายประเภท
Kryo kryo = new Kryo ();
kryo . setDefaultSerializer ( TaggedFieldSerializer . class );
kryo . register ( SomeClass . class );
ด้วยโค้ดนี้ สมมติว่าไม่มีซีเรียลไลเซอร์เริ่มต้นที่ตรงกับ SomeClass ระบบจะใช้ TaggedFieldSerializer
คลาสยังสามารถใช้คำอธิบายประกอบ DefaultSerializer ซึ่งจะถูกใช้แทนการเลือกหนึ่งในซีเรียลไลเซอร์เริ่มต้นของ Kryo:
@ DefaultSerializer ( SomeClassSerializer . class )
public class SomeClass {
// ...
}
เพื่อความยืดหยุ่นสูงสุด คุณสามารถแทนที่ Kryo getDefaultSerializer
ได้เพื่อใช้ตรรกะที่กำหนดเองสำหรับการเลือกและสร้างอินสแตนซ์ของซีเรียลไลเซอร์
เมธอด addDefaultSerializer(Class, Class)
ไม่อนุญาตให้มีการกำหนดค่าของซีเรียลไลเซอร์ โรงงานซีเรียลไลเซอร์สามารถตั้งค่าแทนคลาสซีเรียลไลเซอร์ได้ ทำให้โรงงานสามารถสร้างและกำหนดค่าอินสแตนซ์ซีเรียลไลเซอร์แต่ละรายการได้ โรงงานมีไว้สำหรับซีเรียลไลเซอร์ทั่วไป โดยมักจะมีเมธอด getConfig
เพื่อกำหนดค่าซีเรียลไลเซอร์ที่สร้างขึ้น
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 );
โรงงานซีเรียลไลเซอร์มีเมธอด isSupported(Class)
ซึ่งช่วยให้สามารถปฏิเสธการจัดการคลาสได้ แม้ว่าจะตรงกับคลาสก็ตาม ซึ่งช่วยให้โรงงานสามารถตรวจสอบอินเทอร์เฟซหลายรายการหรือใช้ตรรกะอื่นๆ ได้
ในขณะที่ซีเรียลไลเซอร์บางตัวใช้สำหรับคลาสเฉพาะ แต่บางตัวก็สามารถซีเรียลไลซ์คลาสที่แตกต่างกันได้มากมาย Serializers สามารถใช้ Kryo newInstance(Class)
เพื่อสร้างอินสแตนซ์ของคลาสใดก็ได้ ซึ่งทำได้โดยการค้นหาการลงทะเบียนสำหรับชั้นเรียน จากนั้นใช้ ObjectInstantiator ของการลงทะเบียน สามารถระบุตัวสร้างอินสแตนซ์ได้ในการลงทะเบียน
Registration registration = kryo . register ( SomeClass . class );
registration . setInstantiator ( new ObjectInstantiator < SomeClass >() {
public SomeClass newInstance () {
return new SomeClass ( "some constructor arguments" , 1234 );
}
});
หากการลงทะเบียนไม่มีอินสแตนติเอเตอร์ จะมีให้โดย Kryo newInstantiator
หากต้องการปรับแต่งวิธีการสร้างออบเจ็กต์ คุณสามารถแทนที่ Kryo newInstantiator
หรือใช้ InstantiatorStrategy ก็ได้
Kryo จัดเตรียม DefaultInstantiatorStrategy ซึ่งสร้างวัตถุโดยใช้ ReflectASM เพื่อเรียกตัวสร้างอาร์กิวเมนต์เป็นศูนย์ หากเป็นไปไม่ได้ ระบบจะใช้การสะท้อนกลับเพื่อเรียกตัวสร้างอาร์กิวเมนต์เป็นศูนย์ หากยังล้มเหลว ระบบจะส่งข้อยกเว้นหรือลองใช้ InstantiatorStrategy ทางเลือก Reflection ใช้ setAccessible
ดังนั้นตัวสร้างอาร์กิวเมนต์ส่วนตัวที่เป็นศูนย์อาจเป็นวิธีที่ดีในการอนุญาตให้ Kryo สร้างอินสแตนซ์ของคลาสโดยไม่ส่งผลกระทบต่อ API สาธารณะ
DefaultInstantiatorStrategy เป็นวิธีที่แนะนำในการสร้างวัตถุด้วย Kryo มันรันคอนสตรัคเตอร์เหมือนกับที่ทำกับโค้ด Java กลไกทางเลือกนอกภาษายังสามารถใช้เพื่อสร้างวัตถุได้ Objenesis StdInstantiatorStrategy ใช้ API เฉพาะของ JVM เพื่อสร้างอินสแตนซ์ของคลาสโดยไม่ต้องเรียก Constructor ใดๆ เลย การใช้สิ่งนี้เป็นสิ่งที่อันตรายเพราะคลาสส่วนใหญ่คาดหวังว่าคอนสตรัคเตอร์จะถูกเรียก การสร้างวัตถุโดยการข้ามตัวสร้างอาจทำให้วัตถุอยู่ในสถานะที่ไม่ได้เตรียมใช้งานหรือไม่ถูกต้อง คลาสจะต้องได้รับการออกแบบให้สร้างในลักษณะนี้
สามารถกำหนดค่า Kryo ให้ลองใช้ DefaultInstantiatorStrategy ก่อน จากนั้นจึงเลือกใช้ StdInstantiatorStrategy หากจำเป็น
kryo . setInstantiatorStrategy ( new DefaultInstantiatorStrategy ( new StdInstantiatorStrategy ()));
อีกทางเลือกหนึ่งคือ SerializingInstantiatorStrategy ซึ่งใช้กลไกการทำให้เป็นอนุกรมในตัวของ Java เพื่อสร้างอินสแตนซ์ การใช้สิ่งนี้ คลาสจะต้องใช้ java.io.Serializable และตัวสร้างอาร์กิวเมนต์ศูนย์ตัวแรกในคลาสซุปเปอร์จะถูกเรียกใช้ สิ่งนี้ยังข้ามตัวสร้างและเป็นอันตรายด้วยเหตุผลเดียวกันกับ StdInstantiatorStrategy
kryo . setInstantiatorStrategy ( new DefaultInstantiatorStrategy ( new SerializingInstantiatorStrategy ()));
อีกทางหนึ่ง ซีเรียลไลเซอร์ทั่วไปบางตัวจะมีวิธีการที่สามารถแทนที่ได้เพื่อปรับแต่งการสร้างอ็อบเจ็กต์สำหรับประเภทเฉพาะ แทนที่จะเรียก 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 );
}
});
ซีเรียลไลเซอร์บางตัวมีเมธอด writeHeader
ที่สามารถแทนที่ได้เพื่อเขียนข้อมูลที่จำเป็นใน create
ในเวลาที่เหมาะสม
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 ));
}
}
หากซีเรียลไลเซอร์ไม่มี writeHeader
การเขียนข้อมูลเพื่อ create
สามารถทำได้ใน 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 ());
}
}
แม้ว่าตัวซีเรียลไลเซอร์จะรู้คลาสที่คาดหวังสำหรับค่า (เช่น คลาสของฟิลด์) หากคลาสที่เป็นรูปธรรมของค่านั้นไม่ใช่คลาสสุดท้าย ตัวซีเรียลไลเซอร์จะต้องเขียน ID คลาสก่อน จากนั้นจึงเขียนค่า คลาสสุดท้ายสามารถถูกทำให้เป็นอนุกรมได้อย่างมีประสิทธิภาพมากขึ้น เนื่องจากคลาสเหล่านั้นไม่ใช่แบบโพลีมอร์ฟิก
Kryo isFinal
ใช้เพื่อพิจารณาว่าคลาสนั้นถือเป็นที่สิ้นสุดหรือไม่ วิธีนี้สามารถแทนที่ได้เพื่อให้คืนค่าเป็นจริง แม้ว่าจะไม่ใช่ประเภทสุดท้ายก็ตาม ตัวอย่างเช่น หากแอปพลิเคชันใช้ ArrayList อย่างกว้างขวางแต่ไม่เคยใช้คลาสย่อย ArrayList เลย ถือว่า ArrayList เป็นขั้นสุดท้ายอาจทำให้ FieldSerializer บันทึก 1-2 ไบต์ต่อฟิลด์ ArrayList
Kryo สามารถทำให้การปิด Java 8+ เป็นอนุกรมที่ใช้ java.io.Serializable ได้ โดยมีคำเตือนบางประการ การปิดแบบอนุกรมบน JVM หนึ่งอาจล้มเหลวในการดีซีเรียลไลซ์บน JVM อื่น
Kryo isClosure
ใช้เพื่อตรวจสอบว่าคลาสปิดอยู่หรือไม่ หากเป็นเช่นนั้น ClosedSerializer.Closure จะถูกใช้เพื่อค้นหาการลงทะเบียนคลาสแทนคลาสของการปิด หากต้องการทำให้การปิดเป็นอนุกรม จะต้องลงทะเบียนคลาสต่อไปนี้: ClosedSerializer.Closure, Object[] และ Class นอกจากนี้ จะต้องลงทะเบียนคลาสการจับของการปิดด้วย
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 );
การปิดการทำให้เป็นอนุกรมซึ่งไม่ได้ใช้ Serializable สามารถทำได้ด้วยความพยายามบางอย่าง
Kryo รองรับสตรีม ดังนั้นจึงเป็นเรื่องเล็กน้อยที่จะใช้การบีบอัดหรือการเข้ารหัสกับไบต์ซีเรียลไลซ์ทั้งหมด:
OutputStream outputStream = new DeflaterOutputStream ( new FileOutputStream ( "file.bin" ));
Output output = new Output ( outputStream );
Kryo kryo = new Kryo ();
kryo . writeObject ( output , object );
output . close ();
หากจำเป็น สามารถใช้ซีเรียลไลเซอร์เพื่อบีบอัดหรือเข้ารหัสไบต์เพียงชุดย่อยของไบต์สำหรับกราฟออบเจ็กต์ ตัวอย่างเช่น ดู DeflateSerializer หรือ BlowfishSerializer ซีเรียลไลเซอร์เหล่านี้รวมซีเรียลไลเซอร์อื่นเพื่อเข้ารหัสและถอดรหัสไบต์
คลาสนามธรรมของ Serializer กำหนดวิธีการที่จะเปลี่ยนจากอ็อบเจ็กต์เป็นไบต์ และไบต์ไปยังอ็อบเจ็กต์
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 มีเพียงสองวิธีที่ต้องใช้ write
เขียนวัตถุเป็นไบต์ไปยังเอาต์พุต read
สร้างอินสแตนซ์ใหม่ของวัตถุและอ่านจากอินพุตเพื่อเติมข้อมูล
เมื่อ Kryo ถูกใช้เพื่ออ่านวัตถุที่ซ้อนกันใน Serializer read
ดังนั้น reference
Kryo จะต้องถูกเรียกด้วยวัตถุหลักก่อน หากเป็นไปได้ที่วัตถุที่ซ้อนกันจะอ้างอิงวัตถุหลัก ไม่จำเป็นต้องเรียก reference
Kryo หากวัตถุที่ซ้อนกันไม่สามารถอ้างอิงวัตถุหลักได้ หากไม่ได้ใช้ Kryo สำหรับวัตถุที่ซ้อนกัน หรือหากไม่ได้ใช้การอ้างอิง หากอ็อบเจ็กต์ที่ซ้อนกันสามารถใช้ซีเรียลไลเซอร์เดียวกันได้ซีเรียลไลเซอร์จะต้องกลับมาใหม่
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 ;
}
โดยปกติแล้ว Serializers ไม่ควรใช้งาน Serializers อื่นๆ โดยตรง แต่ควรใช้วิธีอ่านและเขียน Kryo แทน สิ่งนี้ทำให้ Kryo สามารถประสานการทำให้เป็นอนุกรมและจัดการคุณสมบัติต่างๆ เช่น การอ้างอิงและอ็อบเจ็กต์ null บางครั้งซีเรียลไลเซอร์จะรู้ว่าซีเรียลไลเซอร์ตัวใดที่จะใช้กับอ็อบเจ็กต์ที่ซ้อนกัน ในกรณีนั้น ควรใช้วิธีการอ่านและเขียนของ Kryo ซึ่งยอมรับซีเรียลไลเซอร์
หากวัตถุอาจเป็นโมฆะ:
Serializer serializer = ...
kryo . writeObjectOrNull ( output , object , serializer );
SomeClass object = kryo . readObjectOrNull ( input , SomeClass . class , serializer );
หากวัตถุไม่สามารถเป็นค่าว่างได้:
Serializer serializer = ...
kryo . writeObject ( output , object , serializer );
SomeClass object = kryo . readObject ( input , SomeClass . class , serializer );
ในระหว่างการทำให้เป็นอนุกรม Kryo getDepth
จะให้ความลึกปัจจุบันของกราฟวัตถุ
เมื่อการทำให้เป็นอนุกรมล้มเหลว สามารถส่ง KryoException พร้อมข้อมูลการติดตามการทำให้เป็นอนุกรมเกี่ยวกับตำแหน่งในกราฟวัตถุที่เกิดข้อยกเว้น เมื่อใช้ซีเรียลไลเซอร์แบบซ้อน KryoException สามารถตรวจสอบได้เพื่อเพิ่มข้อมูลการติดตามการทำให้เป็นอนุกรม
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 ;
}
}
ตัวซีเรียลไลเซอร์ Kryo จัดเตรียมการใช้ call stack เมื่อซีเรียลไลซ์อ็อบเจ็กต์ที่ซ้อนกัน Kryo ลดการเรียกสแต็กให้เหลือน้อยที่สุด แต่สแต็กโอเวอร์โฟลว์สามารถเกิดขึ้นได้สำหรับกราฟวัตถุที่มีความลึกมาก นี่เป็นปัญหาทั่วไปสำหรับไลบรารีการทำให้เป็นอนุกรมส่วนใหญ่ รวมถึงการทำให้เป็นอนุกรม Java ในตัว สามารถเพิ่มขนาดสแต็กได้โดยใช้ -Xss
แต่โปรดทราบว่าสิ่งนี้ใช้ได้กับทุกเธรด ขนาดสแต็กขนาดใหญ่ใน JVM ที่มีเธรดจำนวนมากอาจใช้หน่วยความจำจำนวนมาก
Kryo setMaxDepth
สามารถใช้เพื่อจำกัดความลึกสูงสุดของกราฟวัตถุ วิธีนี้สามารถป้องกันไม่ให้ข้อมูลที่เป็นอันตรายทำให้เกิดสแต็กล้นได้
ตามค่าเริ่มต้น ตัวซีเรียลไลเซอร์จะไม่ได้รับค่าว่าง แต่ Kryo จะเขียนไบต์ตามความจำเป็นเพื่อแสดงว่าเป็นค่าว่างหรือไม่เป็นค่าว่าง หากซีเรียลไลเซอร์สามารถมีประสิทธิภาพมากขึ้นโดยการจัดการค่า null เอง มันก็สามารถเรียก Serializer setAcceptsNull(true)
ได้ นอกจากนี้ยังสามารถใช้เพื่อหลีกเลี่ยงการเขียนไบต์ที่แสดงถึงค่าว่างเมื่อทราบว่าอินสแตนซ์ทั้งหมดที่ซีเรียลไลเซอร์จะจัดการจะไม่มีวันเป็นค่าว่าง
Kryo getGenerics
ให้ข้อมูลประเภททั่วไปเพื่อให้ซีเรียลไลเซอร์มีประสิทธิภาพมากขึ้น โดยทั่วไปจะใช้เพื่อหลีกเลี่ยงการเขียนคลาสเมื่อคลาสพารามิเตอร์ประเภทถือเป็นที่สิ้นสุด
การอนุมานประเภททั่วไปถูกเปิดใช้งานตามค่าเริ่มต้น และสามารถปิดใช้งานได้ด้วย Kryo setOptimizedGenerics(false)
การปิดใช้งานการปรับให้เหมาะสมทั่วไปสามารถเพิ่มประสิทธิภาพได้โดยใช้ขนาดซีเรียลไลซ์ที่ใหญ่ขึ้น
หากคลาสมีพารามิเตอร์ประเภทเดียว nextGenericClass
จะส่งกลับคลาสพารามิเตอร์ประเภท หรือเป็นโมฆะหากไม่มี หลังจากอ่านหรือเขียนอ็อบเจ็กต์ที่ซ้อนกันแล้ว จะต้องเรียก popGenericType
ดูตัวอย่างที่ CollectionSerializer
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 ;
}
}
สำหรับคลาสที่มีพารามิเตอร์หลายประเภท nextGenericTypes
จะส่งกลับอาร์เรย์ของอินสแตนซ์ GenericType และ resolve
จะใช้เพื่อรับคลาสสำหรับ GenericType แต่ละรายการ หลังจากอ่านหรือเขียนอ็อบเจ็กต์ที่ซ้อนกันแล้ว จะต้องเรียก popGenericType
ดูตัวอย่าง MapSerializer
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 ;
}
}
สำหรับซีเรียลไลเซอร์ที่ส่งผ่านข้อมูลพารามิเตอร์ประเภทสำหรับอ็อบเจ็กต์ที่ซ้อนกันในกราฟวัตถุ (การใช้งานที่ค่อนข้างสูง) GenericsHierarchy ลำดับแรกจะถูกใช้เพื่อจัดเก็บพารามิเตอร์ประเภทสำหรับคลาส ในระหว่างการทำให้เป็นอนุกรม Generics pushTypeVariables
จะถูกเรียกก่อนที่ประเภททั่วไปจะได้รับการแก้ไข (ถ้ามี) หากส่งคืน >0 จะต้องตามด้วย Generics popTypeVariables
ดูตัวอย่าง FieldSerializer
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 ;
}
}
แทนที่จะใช้ซีเรียลไลเซอร์ คลาสสามารถเลือกที่จะทำซีเรียลไลซ์ของตัวเองได้โดยใช้ KryoSerializable (คล้ายกับ 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 );
}
}
แน่นอนว่าต้องสร้างอินสแตนซ์ไว้ก่อนจึงจะสามารถเรียก read
ได้ ดังนั้นคลาสจึงไม่สามารถควบคุมการสร้างของตัวเองได้ คลาส KryoSerializable จะใช้ตัวสร้างอนุกรมเริ่มต้น KryoSerializableSerializer ซึ่งใช้ Kryo newInstance
เพื่อสร้างอินสแตนซ์ใหม่ การเขียนซีเรียลไลเซอร์ของคุณเองเพื่อปรับแต่งกระบวนการ เรียกเมธอดก่อนหรือหลังซีเรียลไลซ์ ฯลฯ เป็นเรื่องเล็กน้อย
Serializers รองรับการคัดลอกเฉพาะในกรณีที่ copy
ถูกแทนที่ เช่นเดียวกับ Serializer read
เมธอดนี้มีตรรกะในการสร้างและกำหนดค่าสำเนา เช่นเดียวกับ read
จะต้องเรียก reference
Kryo ก่อนที่จะใช้ Kryo เพื่อคัดลอกวัตถุลูก หากวัตถุลูกใด ๆ สามารถอ้างอิงวัตถุแม่ได้
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 ;
}
}
แทนที่จะใช้ซีเรียลไลเซอร์ คลาสสามารถใช้ KryoCopyable เพื่อทำการคัดลอกของตนเองได้:
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)
สามารถใช้เมื่อประเภทไม่เปลี่ยนรูป ในกรณีนั้น ไม่จำเป็นต้องใช้ copy
Serializer -- การใช้งาน copy
เริ่มต้นจะส่งคืนออบเจ็กต์ต้นฉบับ
กฎทั่วไปต่อไปนี้ใช้กับการกำหนดหมายเลขเวอร์ชันของ Kryo:
การอัพเกรดการพึ่งพาใด ๆ เป็นเหตุการณ์ที่สำคัญ แต่ห้องสมุดการทำให้เป็นอนุกรมมีแนวโน้มที่จะแตกหักมากกว่าการพึ่งพาส่วนใหญ่ เมื่ออัพเกรด Kryo ตรวจสอบความแตกต่างของเวอร์ชันและทดสอบเวอร์ชันใหม่อย่างละเอียดในแอปพลิเคชันของคุณเอง เราพยายามทำให้ปลอดภัยและง่ายที่สุด
kryo serializers ที่จัดทำโดยค่าเริ่มต้นสมมติว่า Java จะถูกใช้สำหรับ deserialization ดังนั้นพวกเขาจึงไม่ได้กำหนดรูปแบบที่เขียนไว้อย่างชัดเจน Serializers สามารถเขียนได้โดยใช้รูปแบบมาตรฐานที่อ่านได้ง่ายขึ้นโดยภาษาอื่น ๆ แต่สิ่งนี้ไม่ได้ให้โดยค่าเริ่มต้น
สำหรับความต้องการบางอย่างเช่นการจัดเก็บระยะยาวของไบต์ที่เป็นอนุกรมมันอาจเป็นสิ่งสำคัญที่การทำให้เป็นอนุกรมจัดการการเปลี่ยนแปลงในชั้นเรียน สิ่งนี้เรียกว่าความเข้ากันได้ไปข้างหน้า (การอ่านไบต์ที่เป็นอนุกรมโดยคลาสใหม่กว่า) และความเข้ากันได้ย้อนหลัง (การอ่านไบต์ที่เป็นอนุกรมโดยคลาสที่เก่ากว่า) Kryo ให้ serializers ทั่วไปสองสามตัวซึ่งใช้วิธีการที่แตกต่างกันในการจัดการความเข้ากันได้ serializers เพิ่มเติมสามารถพัฒนาได้อย่างง่ายดายสำหรับความเข้ากันได้ไปข้างหน้าและข้างหลังเช่น serializer ที่ใช้สคีมาที่เขียนด้วยมือภายนอก
เมื่อชั้นเรียนเปลี่ยนมากกว่า serializer สามารถจัดการได้ Serializer สามารถเขียนเพื่อถ่ายโอนข้อมูลไปยังคลาสที่แตกต่างกัน การใช้คลาสเก่าทั้งหมดในรหัสแอปพลิเคชันควรถูกแทนที่ด้วยคลาสใหม่ ชั้นเรียนเก่าถูกเก็บไว้สำหรับ serializer นี้เท่านั้น
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 ให้ตัวเลือกการกำหนดค่าที่หลากหลายและระดับความเข้ากันได้มากมาย serializers เพิ่มเติมสามารถพบได้ในโครงการ Kryo-Serializers Sister ซึ่งเป็นเจ้าภาพ serializers ที่เข้าถึง API ส่วนตัวหรือไม่ปลอดภัยอย่างสมบูรณ์แบบสำหรับ JVM ทั้งหมด serializers เพิ่มเติมสามารถพบได้ในส่วนลิงก์
FieldSerializer ทำงานโดยการทำให้เป็นอนุกรมแต่ละเขตข้อมูลที่ไม่ผ่านการขนส่ง มันสามารถทำให้เป็นอนุกรม pojos และคลาสอื่น ๆ อีกมากมายโดยไม่ต้องกำหนดค่าใด ๆ ฟิลด์ที่ไม่ใช่แบบสาธารณะทั้งหมดจะถูกเขียนและอ่านโดยค่าเริ่มต้นดังนั้นจึงเป็นสิ่งสำคัญที่จะประเมินแต่ละชั้นเรียนที่จะเป็นอนุกรม หากฟิลด์เป็นสาธารณะการทำให้เป็นอนุกรมอาจเร็วขึ้น
FieldSerializer มีประสิทธิภาพโดยการเขียนเฉพาะข้อมูลภาคสนามโดยไม่มีข้อมูลสคีมาใด ๆ โดยใช้ไฟล์คลาส Java เป็นสคีมา ไม่รองรับการเพิ่มลบหรือเปลี่ยนประเภทของฟิลด์โดยไม่ทำให้ไบต์ต่อเนื่องเป็นโมฆะก่อนหน้านี้ การเปลี่ยนชื่อเขตข้อมูลจะได้รับอนุญาตเฉพาะในกรณีที่ไม่เปลี่ยนลำดับตัวอักษรของฟิลด์
ข้อเสียความเข้ากันได้ของ FieldSerializer สามารถยอมรับได้ในหลาย ๆ สถานการณ์เช่นเมื่อส่งข้อมูลผ่านเครือข่าย แต่อาจไม่ใช่ทางเลือกที่ดีสำหรับการจัดเก็บข้อมูลระยะยาวเนื่องจากคลาส Java ไม่สามารถพัฒนาได้ ในหลายกรณี TaggedFieldSerializer เป็นตัวเลือกที่ดีกว่า
การตั้งค่า | คำอธิบาย | ค่าเริ่มต้น |
---|---|---|
fieldsCanBeNull | เมื่อเท็จสันนิษฐานว่าไม่มีค่าฟิลด์เป็นโมฆะซึ่งสามารถบันทึก 0-1 ไบต์ต่อฟิลด์ | จริง |
setFieldsAsAccessible | เมื่อเป็นจริงฟิลด์ที่ไม่ผ่านการขนส่งทั้งหมด (รวมถึงเขตข้อมูลส่วนตัว) จะได้รับการจัดลำดับและ setAccessible หากจำเป็น หากเป็นเท็จเฉพาะฟิลด์ใน API สาธารณะจะได้รับการจัดลำดับ | จริง |
ignoreSyntheticFields | ถ้าเป็นจริงเขตข้อมูลสังเคราะห์ (สร้างโดยคอมไพเลอร์สำหรับการกำหนดขอบเขต) จะเป็นอนุกรม | เท็จ |
fixedFieldTypes | หากเป็นจริงจะถือว่าคอนกรีตทุกประเภทของค่าฟิลด์ตรงกับประเภทของฟิลด์ สิ่งนี้จะช่วยขจัดความจำเป็นในการเขียน ID คลาสสำหรับค่าฟิลด์ | เท็จ |
copyTransient | หากเป็นจริงฟิลด์ชั่วคราวทั้งหมดจะถูกคัดลอก | จริง |
serializeTransient | หากเป็นจริงเขตข้อมูลชั่วคราวจะได้รับการจัดลำดับ | เท็จ |
variableLengthEncoding | หากเป็นจริงค่าความยาวตัวแปรจะถูกใช้สำหรับฟิลด์ Int และยาว | จริง |
extendedFieldNames | หากเป็นจริงชื่อฟิลด์จะถูกนำหน้าโดยชั้นเรียนประกาศของพวกเขา สิ่งนี้สามารถหลีกเลี่ยงความขัดแย้งเมื่อคลาสย่อยมีฟิลด์ที่มีชื่อเดียวกันกับคลาส Super | เท็จ |
FieldSerializer จัดเตรียมฟิลด์ที่จะเป็นอนุกรม สามารถลบฟิลด์ออกได้ดังนั้นจึงไม่ได้รับการจัดลำดับ สามารถกำหนดค่าฟิลด์เพื่อให้การจัดลำดับมีประสิทธิภาพมากขึ้น
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 ());
การตั้งค่า | คำอธิบาย | ค่าเริ่มต้น |
---|---|---|
canBeNull | เมื่อเท็จจะถือว่าค่าฟิลด์จะไม่เป็นโมฆะซึ่งสามารถบันทึก 0-1 ไบต์ | จริง |
valueClass | ตั้งค่าคลาสคอนกรีตและ serializer เพื่อใช้สำหรับค่าฟิลด์ สิ่งนี้จะช่วยขจัดความจำเป็นในการเขียน ID คลาสสำหรับค่า หากคลาสของค่าฟิลด์เป็น wrapper ดั้งเดิม, primitive wrapper หรือสุดท้ายการตั้งค่านี้จะเริ่มต้นไปยังคลาสของฟิลด์ | โมฆะ |
serializer | ตั้งค่า serializer ให้ใช้สำหรับค่าฟิลด์ หากมีการตั้งค่า serializer serializers บางตัวต้องการให้คลาสค่าถูกตั้งค่าด้วย หาก NULL, serializer ที่ลงทะเบียนกับ Kryo สำหรับชั้นเรียนของค่าฟิลด์จะถูกใช้ | โมฆะ |
variableLengthEncoding | หากเป็นจริงจะมีการใช้ค่าความยาวตัวแปร สิ่งนี้ใช้กับเขตข้อมูล Int หรือยาวเท่านั้น | จริง |
optimizePositive | หากเป็นจริงค่าบวกจะได้รับการปรับให้เหมาะสมสำหรับค่าความยาวตัวแปร สิ่งนี้ใช้กับฟิลด์ Int หรือยาวเท่านั้นเมื่อใช้การเข้ารหัสความยาวตัวแปร | จริง |
คำอธิบายประกอบสามารถใช้ในการกำหนดค่า serializers สำหรับแต่ละฟิลด์
คำอธิบายประกอบ | คำอธิบาย |
---|---|
@Bind | ตั้งค่าการตั้งค่า CachedField สำหรับฟิลด์ใด ๆ |
@CollectionBind | ตั้งค่าการตั้งค่า CollectionsERIalizer สำหรับฟิลด์คอลเลกชัน |
@MapBind | ตั้งค่าการตั้งค่า mapserializer สำหรับฟิลด์แผนที่ |
@NotNull | ทำเครื่องหมายสนามว่าไม่เคยเป็นโมฆะ |
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 ขยาย FieldSerializer และให้ความเข้ากันได้ย้อนหลัง ซึ่งหมายความว่าสามารถเพิ่มฟิลด์โดยไม่ต้องใช้ไบต์ที่เป็นอนุกรมก่อนหน้านี้ ไม่รองรับการลบเปลี่ยนชื่อหรือเปลี่ยนประเภทของฟิลด์
เมื่อมีการเพิ่มฟิลด์จะต้องมีคำอธิบายประกอบ @Since(int)
เพื่อระบุเวอร์ชันที่เพิ่มเข้ามาเพื่อให้เข้ากันได้กับไบต์ที่ต่อเนื่องกันก่อนหน้านี้ ค่าคำอธิบายประกอบจะต้องไม่เปลี่ยนแปลง
VersionFieldSerializer เพิ่มค่าใช้จ่ายน้อยมากให้กับ FieldSerializer: ตัวแปรเพิ่มเติมเดียว
การตั้งค่า | คำอธิบาย | ค่าเริ่มต้น |
---|---|---|
compatible | เมื่อเท็จข้อยกเว้นจะถูกโยนเมื่ออ่านวัตถุที่มีเวอร์ชันอื่น เวอร์ชันของวัตถุเป็นเวอร์ชันสูงสุดของฟิลด์ใด ๆ | จริง |
VersionFieldSerializer ยังสืบทอดการตั้งค่าทั้งหมดของ FieldSerializer
TaggedFieldSerializer ขยาย FieldSerializer เพื่อให้ความเข้ากันได้แบบย้อนหลังและความเข้ากันได้ไปข้างหน้าเป็นตัวเลือก ซึ่งหมายความว่าฟิลด์สามารถเพิ่มหรือเปลี่ยนชื่อและถูกลบออกโดยไม่ต้องเป็นตัวเลือกโดยไม่ทำให้ไบต์ที่เป็นอนุกรมก่อนหน้านี้ ไม่รองรับการเปลี่ยนประเภทของฟิลด์
เฉพาะฟิลด์ที่มีคำอธิบายประกอบ @Tag(int)
เท่านั้น ค่าแท็กฟิลด์จะต้องไม่ซ้ำกันทั้งภายในคลาสและคลาสสุดยอดทั้งหมด ข้อยกเว้นจะถูกโยนลงหากพบค่าแท็กซ้ำ
ความเข้ากันได้ไปข้างหน้าและย้อนหลังและประสิทธิภาพการทำให้เป็นอนุกรมขึ้นอยู่กับการตั้งค่า readUnknownTagData
และ chunkedEncoding
นอกจากนี้ตัวแปรจะถูกเขียนก่อนแต่ละฟิลด์สำหรับค่าแท็ก
เมื่อ readUnknownTagData
และ chunkedEncoding
เป็นเท็จฟิลด์จะต้องไม่ถูกลบออก แต่สามารถใช้คำอธิบายประกอบ @Deprecated
ได้ ฟิลด์ที่เลิกใช้แล้วจะอ่านเมื่ออ่านไบต์เก่า แต่ไม่ได้เขียนไปยังไบต์ใหม่ ชั้นเรียนสามารถพัฒนาได้โดยการอ่านค่าของฟิลด์ที่เลิกใช้แล้วและเขียนที่อื่น สามารถเปลี่ยนชื่อฟิลด์และ/หรือสร้างความเป็นส่วนตัวเพื่อลดความยุ่งเหยิงในชั้นเรียน (เช่น ignored1
, ignored2
)
TaggedFieldSerializer (พร้อม readUnknownTagData
และ chunkedEncoding
false) เป็น serializer ที่แนะนำสำหรับชั้นเรียนส่วนใหญ่ที่สามารถใส่คำอธิบายประกอบฟิลด์ ช่วยให้ชั้นเรียนพัฒนาและฟิลด์จะถูกลบออกจากข้อมูลที่เป็นอนุกรม (ผ่านการเสื่อมราคา) ตอบสนองความต้องการของแอปพลิเคชันส่วนใหญ่โดยไม่เพิ่มขนาดให้มากขึ้น
การตั้งค่า | คำอธิบาย | ค่าเริ่มต้น |
---|---|---|
readUnknownTagData | เมื่อพบแท็กที่เป็นเท็จและไม่ทราบข้อยกเว้นจะถูกโยนหรือหาก chunkedEncoding เป็นจริงข้อมูลจะถูกข้ามเมื่อเป็นจริงคลาสสำหรับแต่ละค่าฟิลด์จะถูกเขียนก่อนค่า เมื่อพบแท็กที่ไม่รู้จักความพยายามในการอ่านข้อมูลจะถูกสร้างขึ้น สิ่งนี้ใช้ในการข้ามข้อมูลและหากมีการเปิดใช้งานการอ้างอิงค่าอื่น ๆ ในกราฟวัตถุอ้างอิงว่าข้อมูลยังคงสามารถลดลงได้ หากการอ่านข้อมูลล้มเหลว (เช่นคลาสไม่เป็นที่รู้จักหรือถูกลบออก) จากนั้นข้อยกเว้นจะถูกโยนหรือถ้า chunkedEncoding เป็นจริงข้อมูลจะถูกข้ามไม่ว่าในกรณีใดข้อมูลหากข้อมูลถูกข้ามและเปิดใช้งานการอ้างอิงการอ้างอิงใด ๆ ในข้อมูลที่ข้ามจะไม่ถูกอ่านและ deserialization เพิ่มเติมอาจได้รับการอ้างอิงที่ไม่ถูกต้องและล้มเหลว | เท็จ |
chunkedEncoding | เมื่อจริงฟิลด์จะถูกเขียนด้วยการเข้ารหัสแบบก้อนเพื่อให้ข้อมูลฟิลด์ที่ไม่รู้จักถูกข้าม สิ่งนี้ส่งผลกระทบต่อประสิทธิภาพ | เท็จ |
chunkSize | ขนาดสูงสุดของแต่ละก้อนสำหรับการเข้ารหัสก้อน | 1,024 |
TaggedFieldSerializer ยังสืบทอดการตั้งค่าทั้งหมดของ FieldSerializer
CompatibleFieldSerializer ขยาย FieldSerializer เพื่อให้ความเข้ากันได้ทั้งไปข้างหน้าและย้อนหลัง ซึ่งหมายความว่าฟิลด์สามารถเพิ่มหรือลบออกได้โดยไม่ทำให้ไบต์ต่อเนื่องก่อนหน้านี้เป็นโมฆะ ไม่รองรับการเปลี่ยนชื่อหรือเปลี่ยนประเภทของฟิลด์ เช่นเดียวกับ FieldSerializer สามารถทำให้คลาสส่วนใหญ่ได้โดยไม่จำเป็นต้องมีคำอธิบายประกอบ
ความเข้ากันได้ไปข้างหน้าและย้อนหลังและประสิทธิภาพการทำให้เป็นอนุกรมขึ้นอยู่กับการตั้งค่า readUnknownFieldData
และ chunkedEncoding
นอกจากนี้ครั้งแรกที่ชั้นเรียนถูกพบในไบต์ที่เป็นอนุกรมสคีมาง่าย ๆ จะถูกเขียนขึ้นซึ่งมีสตริงชื่อฟิลด์ เนื่องจากข้อมูลฟิลด์ถูกระบุด้วยชื่อหากคลาส Super มีฟิลด์ที่มีชื่อเดียวกันกับคลาสย่อย extendedFieldNames
จะต้องเป็นจริง
การตั้งค่า | คำอธิบาย | ค่าเริ่มต้น |
---|---|---|
readUnknownFieldData | เมื่อพบฟิลด์เท็จและไม่ทราบฟิลด์ข้อยกเว้นจะถูกโยนหรือหาก chunkedEncoding เป็นจริงข้อมูลจะถูกข้ามเมื่อเป็นจริงคลาสสำหรับแต่ละค่าฟิลด์จะถูกเขียนก่อนค่า เมื่อพบฟิลด์ที่ไม่รู้จักความพยายามในการอ่านข้อมูลจะถูกสร้างขึ้น สิ่งนี้ใช้ในการข้ามข้อมูลและหากมีการเปิดใช้งานการอ้างอิงค่าอื่น ๆ ในกราฟวัตถุอ้างอิงว่าข้อมูลยังคงสามารถลดลงได้ หากการอ่านข้อมูลล้มเหลว (เช่นคลาสไม่เป็นที่รู้จักหรือถูกลบออก) จากนั้นข้อยกเว้นจะถูกโยนหรือถ้า chunkedEncoding เป็นจริงข้อมูลจะถูกข้ามไม่ว่าในกรณีใดข้อมูลหากข้อมูลถูกข้ามและเปิดใช้งานการอ้างอิงการอ้างอิงใด ๆ ในข้อมูลที่ข้ามจะไม่ถูกอ่านและ deserialization เพิ่มเติมอาจได้รับการอ้างอิงที่ไม่ถูกต้องและล้มเหลว | จริง |
chunkedEncoding | เมื่อจริงฟิลด์จะถูกเขียนด้วยการเข้ารหัสแบบก้อนเพื่อให้ข้อมูลฟิลด์ที่ไม่รู้จักถูกข้าม สิ่งนี้ส่งผลกระทบต่อประสิทธิภาพ | เท็จ |
chunkSize | ขนาดสูงสุดของแต่ละก้อนสำหรับการเข้ารหัสก้อน | 1,024 |
CompatiblefieldSerializer ยังสืบทอดการตั้งค่าทั้งหมดของ FieldSerializer
Beanserializer นั้นคล้ายกับ FieldSerializer มากยกเว้นใช้วิธี Bean Getter และ Setter มากกว่าการเข้าถึงภาคสนามโดยตรง ช้าลงเล็กน้อย แต่อาจปลอดภัยกว่าเพราะใช้ API สาธารณะเพื่อกำหนดค่าวัตถุ เช่นเดียวกับ FieldSerializer ไม่มีความเข้ากันได้ไปข้างหน้าหรือย้อนกลับ
collectionserializer ทำให้วัตถุที่ใช้งานอินเตอร์เฟส java.util.collection
การตั้งค่า | คำอธิบาย | ค่าเริ่มต้น |
---|---|---|
elementsCanBeNull | เมื่อเท็จจะสันนิษฐานว่าไม่มีองค์ประกอบในคอลเลกชันเป็นโมฆะซึ่งสามารถประหยัด 0-1 ไบต์ต่อองค์ประกอบ | จริง |
elementClass | ตั้งค่าคลาสคอนกรีตให้ใช้สำหรับแต่ละองค์ประกอบในคอลเลกชัน สิ่งนี้จะช่วยขจัดความจำเป็นในการเขียน ID คลาสสำหรับแต่ละองค์ประกอบ หากคลาสองค์ประกอบเป็นที่รู้จัก (เช่นผ่านสามัญ) และเสื้อคลุมดั้งเดิม, wrapper ดั้งเดิมหรือสุดท้าย, collectionserializer จะไม่เขียน ID คลาสแม้ว่าการตั้งค่านี้จะเป็นโมฆะ | โมฆะ |
elementSerializer | ตั้งค่า serializer ให้ใช้สำหรับทุกองค์ประกอบในคอลเลกชัน หากมีการตั้งค่า serializer serializers บางตัวต้องการให้คลาสค่าถูกตั้งค่าด้วย หาก NULL, serializer ที่ลงทะเบียนกับ Kryo สำหรับคลาสของแต่ละองค์ประกอบจะถูกใช้ | โมฆะ |
Mapserializer ทำให้วัตถุที่ใช้งานอินเตอร์เฟส java.util.map
การตั้งค่า | คำอธิบาย | ค่าเริ่มต้น |
---|---|---|
keysCanBeNull | เมื่อเท็จสันนิษฐานว่าไม่มีปุ่มในแผนที่เป็นโมฆะซึ่งสามารถบันทึก 0-1 ไบต์ต่อรายการ | จริง |
valuesCanBeNull | เมื่อเท็จสันนิษฐานว่าไม่มีค่าในแผนที่เป็นโมฆะซึ่งสามารถบันทึก 0-1 ไบต์ต่อรายการ | จริง |
keyClass | ตั้งค่าคลาสคอนกรีตให้ใช้สำหรับทุกคีย์ในแผนที่ สิ่งนี้จะช่วยขจัดความจำเป็นในการเขียน ID คลาสสำหรับแต่ละคีย์ | โมฆะ |
valueClass | ตั้งค่าคลาสคอนกรีตให้ใช้สำหรับทุกค่าในแผนที่ สิ่งนี้จะช่วยขจัดความจำเป็นในการเขียน ID คลาสสำหรับแต่ละค่า | โมฆะ |
keySerializer | ตั้งค่า serializer ให้ใช้สำหรับทุกคีย์ในแผนที่ หากมีการตั้งค่า Serializer ค่าบาง serializers ต้องการให้คลาสค่าถูกตั้งค่าด้วย หาก NULL, serializer ที่ลงทะเบียนกับ Kryo สำหรับคลาสของแต่ละคีย์จะถูกใช้ | โมฆะ |
valueSerializer | ตั้งค่า serializer ให้ใช้สำหรับทุกค่าในแผนที่ หากมีการตั้งค่า serializer ที่สำคัญบาง serializers ต้องการให้คลาสค่าถูกตั้งค่าด้วย หาก NULL, serializer ที่ลงทะเบียนกับ Kryo สำหรับชั้นเรียนของแต่ละค่าจะถูกใช้ | โมฆะ |
Javaserializer และ Externalizableserializer เป็น kryo serializers ซึ่งใช้การทำให้เป็นอนุกรมในตัวของ Java นี่เป็นเรื่องช้าเช่นเดียวกับการทำให้เป็นอนุกรม Java ปกติ แต่อาจจำเป็นสำหรับชั้นเรียนมรดก
java.io.externalizable และ java.io.serializable ไม่มี serializers เริ่มต้นที่กำหนดโดยค่าเริ่มต้นดังนั้น serializers เริ่มต้นจะต้องตั้งค่าด้วยตนเองหรือ serializers ตั้งค่าเมื่อคลาสถูกลงทะเบียน
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 ใช้ประโยชน์จากห้องสมุดการบันทึกมินล็อกที่มีน้ำหนักเบาต่ำ ระดับการบันทึกสามารถตั้งค่าได้โดยวิธีหนึ่งต่อไปนี้:
Log . ERROR ();
Log . WARN ();
Log . INFO ();
Log . DEBUG ();
Log . TRACE ();
Kryo ไม่ได้เข้าสู่ระบบที่ INFO
(ค่าเริ่มต้น) และระดับสูงกว่า DEBUG
สะดวกในการใช้งานในระหว่างการพัฒนา TRACE
เป็นสิ่งที่ดีที่จะใช้เมื่อทำการดีบักปัญหาเฉพาะ แต่โดยทั่วไปแล้วส่งออกข้อมูลมากเกินไปที่จะออกไป
Minlog รองรับระดับการบันทึกคงที่ซึ่งทำให้คอมไพเลอร์ Java ลบคำสั่งการบันทึกต่ำกว่าระดับนั้นในเวลาที่รวบรวม Kryo จะต้องรวบรวมด้วยขวด minlog ระดับการบันทึกคงที่
Kryo ไม่ปลอดภัย แต่ละเธรดควรมีอินสแตนซ์ Kryo อินพุตและเอาต์พุตของตัวเอง
เนื่องจาก Kryo ไม่ปลอดภัยและสร้างและกำหนดค่าอินสแตนซ์ Kryo จึงค่อนข้างแพงใน ThreadLocal สภาพแวดล้อมแบบมัลติเธรดหรือการรวมกลุ่มอาจได้รับการพิจารณา
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 ();
สำหรับการรวมกลุ่ม Kryo ให้คลาสสระว่ายน้ำซึ่งสามารถรวม Kryo อินพุตเอาท์พุทหรืออินสแตนซ์ของคลาสอื่น ๆ
// 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 );
หาก true
ถูกส่งผ่านเป็นอาร์กิวเมนต์แรกไปยังตัวสร้างพูลพูลใช้การซิงโครไนซ์ภายในและสามารถเข้าถึงได้หลายเธรดพร้อมกัน
หาก true
ถูกส่งผ่านเป็นอาร์กิวเมนต์ที่สองไปยังตัวสร้างพูลสระว่ายน้ำจะเก็บวัตถุโดยใช้ java.lang.ref.softreference สิ่งนี้จะช่วยให้วัตถุในสระว่ายน้ำเก็บขยะเมื่อความดันหน่วยความจำบน JVM สูง สระว่ายน้ำ clean
จะลบการอ้างอิงที่อ่อนนุ่มทั้งหมดที่มีวัตถุถูกเก็บรวบรวม สิ่งนี้สามารถลดขนาดของพูลเมื่อไม่มีการตั้งค่าความจุสูงสุด เมื่อสระว่ายน้ำมีความจุสูงสุดไม่จำเป็นต้องโทร clean
เพราะสระว่ายน้ำ free
จะพยายามลบการอ้างอิงที่ว่างเปล่าหากถึงความจุสูงสุด
พารามิเตอร์พูลที่สามคือความจุสูงสุด หากวัตถุเป็นอิสระและพูลมีจำนวนวัตถุอิสระสูงสุดแล้ววัตถุที่ระบุจะถูกรีเซ็ต แต่ไม่ได้เพิ่มลงในพูล ความจุสูงสุดอาจถูกละเว้นโดยไม่ จำกัด
หากวัตถุใช้พูลพูลสามารถใช้งานได้ reset
ที่สามารถรวมได้จะถูกเรียกเมื่อวัตถุเป็นอิสระ สิ่งนี้ทำให้วัตถุมีโอกาสรีเซ็ตสถานะของตนเพื่อนำกลับมาใช้ใหม่ในอนาคต อีกวิธีหนึ่งคือ reset
พูลสามารถแทนที่เพื่อรีเซ็ตวัตถุ การใช้งานอินพุตและเอาต์พุตสามารถใช้งานได้เพื่อตั้ง position
และ total
เป็น 0 KRYO ไม่สามารถใช้งานได้เนื่องจากสถานะกราฟของวัตถุมักจะรีเซ็ตโดยอัตโนมัติหลังจากการทำให้เป็นอนุกรมแต่ละครั้ง (ดูรีเซ็ต) หากคุณปิดใช้งานการรีเซ็ตอัตโนมัติผ่าน setAutoReset(false)
ตรวจสอบให้แน่ใจว่าคุณเรียก Kryo.reset()
ก่อนที่จะส่งคืนอินสแตนซ์ไปยังพูล
Pool getFree
ส่งคืนจำนวนวัตถุที่มีให้ได้ หากใช้การอ้างอิงที่อ่อนนุ่มหมายเลขนี้อาจรวมถึงวัตถุที่เก็บขยะ clean
อาจใช้ก่อนเพื่อลบการอ้างอิงที่ว่างเปล่าที่ว่างเปล่า
Pool getPeak
ส่งคืนจำนวนวัตถุฟรีสูงสุดตลอดเวลา สิ่งนี้สามารถช่วยตรวจสอบได้ว่าความจุสูงสุดของพูลถูกตั้งค่าอย่างเหมาะสมหรือไม่ สามารถรีเซ็ตได้ตลอดเวลาด้วย resetPeak
Kryo มีการวัดเกณฑ์มาตรฐานที่ใช้ JMH จำนวนหนึ่งและไฟล์ R/GGPLOT2
Kryo สามารถเปรียบเทียบกับห้องสมุดอนุกรมอื่น ๆ อีกมากมายในโครงการ JVM serializers มาตรฐานมีขนาดเล็กลงวันที่และพื้นบ้านแทนที่จะใช้ JMH ดังนั้นจึงน่าเชื่อถือน้อยกว่า นอกจากนี้ยังยากมากที่จะเปรียบเทียบไลบรารีอนุกรมอย่างละเอียดโดยใช้เกณฑ์มาตรฐาน ห้องสมุดมีคุณสมบัติที่แตกต่างกันมากมายและมักจะมีเป้าหมายที่แตกต่างกันดังนั้นพวกเขาอาจเก่งในการแก้ปัญหาที่แตกต่างอย่างสิ้นเชิง เพื่อทำความเข้าใจกับมาตรฐานเหล่านี้รหัสที่กำลังทำงานและข้อมูลที่ถูกอนุกรมควรวิเคราะห์และเปรียบเทียบกับความต้องการเฉพาะของคุณ serializers บางตัวได้รับการปรับปรุงอย่างสูงและใช้หน้ารหัสอื่น ๆ ใช้เพียงไม่กี่บรรทัด นี่เป็นสิ่งที่ดีที่จะแสดงสิ่งที่เป็นไปได้ แต่อาจไม่ใช่การเปรียบเทียบที่เกี่ยวข้องสำหรับหลาย ๆ สถานการณ์
มีหลายโครงการที่ใช้ Kryo มีอยู่ไม่กี่รายการด้านล่าง กรุณาส่งคำขอดึงหากคุณต้องการโครงการของคุณที่นี่