Kryo هو إطار تسلسلي للرسم البياني للكائنات الثنائية سريع وفعال لجافا. أهداف المشروع هي السرعة العالية والحجم المنخفض وواجهة برمجة التطبيقات (API) سهلة الاستخدام. يعد المشروع مفيدًا في أي وقت تحتاج فيه الكائنات إلى الاستمرار، سواء في ملف أو قاعدة بيانات أو عبر الشبكة.
يمكن لـ Kryo أيضًا إجراء نسخ/استنساخ تلقائي عميق وضحل. هذا هو النسخ المباشر من كائن إلى كائن، وليس الاعتراض على البايتات إلى الكائن.
هذه الوثائق مخصصة لإصدار Kryo 5.x. راجع Wiki للإصدار 4.x.
يرجى استخدام القائمة البريدية لـ Kryo للأسئلة والمناقشات والدعم. يرجى قصر استخدام أداة تعقب مشكلات Kryo على الأخطاء والتحسينات، وليس على الأسئلة أو المناقشات أو الدعم.
تنشر Kryo نوعين من القطع الأثرية/الجرار:
تتوفر ملفات Kryo JARs على صفحة الإصدارات وفي Maven Central. أحدث لقطات Kryo، بما في ذلك إنشاءات اللقطات الرئيسية، موجودة في مستودع Sonatype.
لاستخدام أحدث إصدار من 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 باستخدام فئات الإدخال والإخراج. هذه الفئات ليست آمنة لمؤشر الترابط.
فئة الإخراج هي OutputStream التي تكتب البيانات إلى مخزن مؤقت لصفيف البايت. يمكن الحصول على هذا المخزن المؤقت واستخدامه مباشرة، إذا كنت ترغب في استخدام مصفوفة بايت. إذا تم إعطاء الإخراج OutputStream، فسوف يقوم بتدفق البايتات إلى الدفق عندما يصبح المخزن المؤقت ممتلئًا، وإلا يمكن أن ينمو الإخراج المخزن المؤقت الخاص به تلقائيًا. يحتوي الإخراج على العديد من الطرق لكتابة الأوليات والسلاسل بكفاءة إلى البايتات. فهو يوفر وظائف مشابهة لـ DataOutputStream، وBufferedOutputStream، وFilterOutputStream، وByteArrayOutputStream، وكلها في فئة واحدة.
نصيحة: يوفر الإخراج والإدخال جميع وظائف ByteArrayOutputStream. نادرًا ما يكون هناك سبب لتدفق الإخراج إلى ByteArrayOutputStream.
يقوم الإخراج بتخزين البايتات مؤقتًا عند الكتابة إلى OutputStream، لذلك يجب استدعاء flush
أو close
بعد اكتمال الكتابة للتسبب في كتابة البايتات المخزنة مؤقتًا إلى OutputStream. إذا لم يتم توفير الإخراج OutputStream، فإن استدعاء flush
أو close
غير ضروري. على عكس العديد من التدفقات، يمكن إعادة استخدام مثيل الإخراج عن طريق تعيين الموضع، أو تعيين مصفوفة بايت جديدة أو دفق.
نصيحة: نظرًا لأن المخازن المؤقتة للإخراج موجودة بالفعل، فلا يوجد سبب لتدفق الإخراج إلى BufferedOutputStream.
يقوم منشئ إخراج الوسيطة الصفرية بإنشاء إخراج غير مهيأ. يجب استدعاء Output setBuffer
قبل أن يتم استخدام الإخراج.
فئة الإدخال هي InputStream التي تقرأ البيانات من المخزن المؤقت لصفيف البايت. يمكن تعيين هذا المخزن المؤقت مباشرة، إذا كانت القراءة من مصفوفة بايت مطلوبة. إذا تم إعطاء الإدخال InputStream، فسوف يملأ المخزن المؤقت من الدفق عند قراءة جميع البيانات الموجودة في المخزن المؤقت. يحتوي الإدخال على العديد من الطرق لقراءة الأوليات والسلاسل بكفاءة من البايتات. فهو يوفر وظائف مشابهة لـ DataInputStream، وBufferedInputStream، وFilterInputStream، وByteArrayInputStream، وكلها في فئة واحدة.
نصيحة: يوفر الإدخال جميع وظائف ByteArrayInputStream. نادرًا ما يكون هناك سبب لقراءة الإدخال من ByteArrayInputStream.
إذا تم استدعاء close
الإدخال، فسيتم إغلاق InputStream الخاص بالإدخال، إن وجد. إذا لم تكن تقرأ من InputStream، فليس من الضروري الاتصال close
. على عكس العديد من التدفقات، يمكن إعادة استخدام مثيل الإدخال عن طريق تعيين الموضع والحد، أو تعيين مصفوفة بايت جديدة أو InputStream.
يقوم منشئ إدخال الوسيطة الصفرية بإنشاء إدخال غير مهيأ. يجب استدعاء الإدخال setBuffer
قبل أن يتم استخدام الإدخال.
تعمل فئتا ByteBufferOutput وByteBufferInput تمامًا مثل الإخراج والإدخال، باستثناء أنهما يستخدمان ByteBuffer بدلاً من مصفوفة البايت.
تعمل الفئات UnsafeOutput وUnsafeInput وUnsafeByteBufferOutput وUnsafeByteBufferInput تمامًا مثل نظيراتها غير الآمنة، باستثناء أنها تستخدم sun.misc.Unsafe للحصول على أداء أعلى في العديد من الحالات. لاستخدام هذه الفئات، يجب أن يكون Util.unsafe
صحيحًا.
الجانب السلبي لاستخدام المخازن المؤقتة غير الآمنة هو أن النهاية الأصلية وتمثيل الأنواع الرقمية للنظام الذي يقوم بالتسلسل يؤثر على البيانات المتسلسلة. على سبيل المثال، ستفشل عملية إلغاء التسلسل إذا تمت كتابة البيانات على X86 وقراءتها على SPARC. وأيضًا، إذا تمت كتابة البيانات باستخدام مخزن مؤقت غير آمن، فيجب قراءتها باستخدام مخزن مؤقت غير آمن.
أكبر اختلاف في الأداء مع المخازن المؤقتة غير الآمنة هو مع المصفوفات البدائية الكبيرة عند عدم استخدام التشفير المتغير الطول. يمكن تعطيل التشفير ذو الطول المتغير للمخازن المؤقتة غير الآمنة أو لحقول محددة فقط (عند استخدام FieldSerializer).
توفر فئات IO طرقًا لقراءة وكتابة القيم المتغيرة الطول (varint) والطويلة (varlong). يتم ذلك عن طريق استخدام البت الثامن من كل بايت للإشارة إلى ما إذا كان هناك المزيد من البايتات، مما يعني أن المتغير يستخدم 1-5 بايت بينما يستخدم varlong 1-9 بايت. يعد استخدام التشفير ذو الطول المتغير أكثر تكلفة ولكنه يجعل البيانات المتسلسلة أصغر بكثير.
عند كتابة قيمة ذات طول متغير، يمكن تحسين القيمة إما للقيم الموجبة أو للقيم السالبة والموجبة. على سبيل المثال، عند تحسين القيم الموجبة، تتم كتابة من 0 إلى 127 في بايت واحد، ومن 128 إلى 16383 في بايتين، وما إلى ذلك. ومع ذلك، فإن الأرقام السالبة الصغيرة هي الحالة الأسوأ عند 5 بايت. عندما لا يتم تحسينها لتكون إيجابية، يتم إزاحة هذه النطاقات إلى النصف. على سبيل المثال، تتم كتابة -64 إلى 63 في بايت واحد، ومن 64 إلى 8191 ومن -65 إلى -8192 في بايتين، وما إلى ذلك.
توفر المخازن المؤقتة للإدخال والإخراج طرقًا لقراءة وكتابة قيم ذات حجم ثابت أو متغير الطول. هناك أيضًا طرق للسماح للمخزن المؤقت بتحديد ما إذا كان سيتم كتابة قيمة ذات حجم ثابت أو قيمة متغيرة الطول. يسمح هذا لكود التسلسل بضمان استخدام تشفير متغير الطول للقيم الشائعة جدًا التي قد تؤدي إلى تضخيم الإخراج إذا تم استخدام حجم ثابت، مع السماح لتكوين المخزن المؤقت بتحديد جميع القيم الأخرى.
طريقة | وصف |
---|---|
كتابة كثافة العمليات (كثافة العمليات) | يكتب كثافة العمليات 4 بايت. |
writeVarInt(int، منطقية) | يكتب كثافة العمليات 1-5 بايت. |
writeInt(int، منطقية) | يكتب إما 4 أو 1-5 بايت int (يقرر المخزن المؤقت). |
الكتابة طويلة (طويلة) | يكتب 8 بايت طويلة. |
writeVarLong (طويل، منطقي) | يكتب 1-9 بايت طويلة. |
writeLong(طويل، منطقي) | يكتب إما 8 أو 1-9 بايت (يقرر المخزن المؤقت). |
لتعطيل تشفير الطول المتغير لجميع القيم، يجب تجاوز الأساليب writeVarInt
و writeVarLong
و readVarInt
و readVarLong
.
قد يكون من المفيد كتابة طول بعض البيانات، ثم البيانات. عندما لا يكون طول البيانات معروفًا مسبقًا، يجب تخزين جميع البيانات مؤقتًا لتحديد طولها، ثم يمكن كتابة الطول، ثم البيانات. إن استخدام مخزن مؤقت واحد كبير لهذا قد يمنع الدفق وقد يتطلب مخزنًا مؤقتًا كبيرًا بشكل غير معقول، وهو ليس مثاليًا.
يعمل التشفير المقسم على حل هذه المشكلة باستخدام مخزن مؤقت صغير. عندما يمتلئ المخزن المؤقت، يتم كتابة طوله، ثم البيانات. هذه قطعة واحدة من البيانات. يتم مسح المخزن المؤقت ويستمر هذا حتى لا يكون هناك المزيد من البيانات للكتابة. القطعة التي يبلغ طولها صفر تشير إلى نهاية القطع.
يوفر Kryo دروسًا لتسهيل استخدام التشفير المجزأ. يتم استخدام 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.
يعد التشفير ذو الطول المتغير أبطأ من القيم الثابتة، خاصة عندما يكون هناك الكثير من البيانات التي تستخدمه.
يستخدم التشفير المقسم مخزنًا مؤقتًا وسيطًا بحيث يضيف نسخة إضافية واحدة من كافة وحدات البايت. قد يكون هذا وحده مقبولاً، ولكن عند استخدامه في جهاز تسلسل إعادة الدخول، يجب على جهاز التسلسل إنشاء 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 );
تقوم كل هذه الطرق أولاً بالعثور على المُسلسِل المناسب لاستخدامه، ثم استخدامه لإجراء تسلسل للكائن أو إلغاء تسلسله. يمكن للمتسلسلات استدعاء هذه الأساليب للتسلسل العودي. تتم معالجة المراجع المتعددة لنفس الكائن والمراجع الدائرية بواسطة Kryo تلقائيًا.
إلى جانب طرق قراءة الكائنات وكتابتها، توفر فئة Kryo طريقة لتسجيل المُسلسلات، وقراءة معرفات الفئة وكتابتها بكفاءة، والتعامل مع الكائنات الفارغة للمُسلسلات التي لا يمكنها قبول القيم الخالية، والتعامل مع قراءة وكتابة مراجع الكائنات (إذا تم تمكينها). وهذا يسمح للمتسلسلين بالتركيز على مهام التسلسل الخاصة بهم.
أثناء اختبار واستكشاف واجهات برمجة تطبيقات Kryo، قد يكون من المفيد كتابة كائن بالبايت، ثم قراءة تلك البايتات مرة أخرى إلى كائن.
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 );
في هذا المثال، يبدأ الإخراج بمخزن مؤقت بسعة 1024 بايت. إذا تمت كتابة المزيد من البايتات إلى الإخراج، فسيزداد حجم المخزن المؤقت بلا حدود. لا يلزم إغلاق الإخراج لأنه لم يتم إعطاؤه 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
للنسخ.
عند تمكين المراجع، تتم كتابة متغير قبل كل كائن في المرة الأولى التي يظهر فيها في الرسم البياني للكائن. بالنسبة للمظاهر اللاحقة لتلك الفئة ضمن نفس الرسم البياني للكائن، تتم كتابة متغير فقط. بعد إلغاء التسلسل، تتم استعادة مراجع الكائنات، بما في ذلك أي مراجع دائرية. يجب أن تدعم المُسلسلات المستخدمة المراجع عن طريق استدعاء reference
Kryo في Serializer read
.
يؤثر تمكين المراجع على الأداء لأن كل كائن تتم قراءته أو كتابته يحتاج إلى التتبع.
تحت الأغطية، يعالج ReferenceResolver كائنات التتبع التي تمت قراءتها أو كتابتها ويوفر معرفات مرجعية int. يتم توفير تطبيقات متعددة:
يمكن تجاوز ReferenceResolver useReferences(Class)
. تقوم بإرجاع قيمة منطقية لتحديد ما إذا كانت المراجع مدعومة لفئة ما. إذا كانت الفئة لا تدعم المراجع، فلن تتم كتابة معرف المرجع المتغير قبل الكائنات من هذا النوع. إذا كانت الفئة لا تحتاج إلى مراجع وظهرت كائنات من هذا النوع في الرسم البياني للكائنات عدة مرات، فيمكن تقليل الحجم المتسلسل بشكل كبير عن طريق تعطيل المراجع الخاصة بتلك الفئة. يقوم محلل المرجع الافتراضي بإرجاع خطأ لجميع الأغلفة والتعدادات الأولية. من الشائع أيضًا إرجاع خطأ للسلسلة والفئات الأخرى، اعتمادًا على الرسوم البيانية للكائنات التي يتم تسلسلها.
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 لجميع جهات التسلسل، لذلك يمكن لجميع جهات التسلسل الوصول إلى هذه البيانات بسهولة.
يشبه Kryo getGraphContext
، ولكن يتم مسحه بعد إجراء تسلسل لكل رسم بياني للكائن أو إلغاء تسلسله. وهذا يجعل من السهل إدارة الحالة ذات الصلة فقط بالرسم البياني للكائن الحالي. على سبيل المثال، يمكن استخدام هذا لكتابة بعض بيانات المخطط في المرة الأولى التي تتم فيها مواجهة فئة في رسم بياني للكائن. راجعCompatibleFieldSerializer للحصول على مثال.
افتراضيًا، يتم استدعاء reset
Kryo بعد إجراء تسلسل لكل رسم بياني للكائن بالكامل. يؤدي هذا إلى إعادة تعيين أسماء الفئات غير المسجلة في محلل الفئة، والإشارات إلى كائنات تم تسلسلها أو إلغاء تسلسلها مسبقًا في محلل المرجع، ومسح سياق الرسم البياني. يمكن استخدام Kryo setAutoReset(false)
لتعطيل reset
الاستدعاء تلقائيًا، مما يسمح لهذه الحالة بالامتداد إلى رسوم بيانية متعددة للكائنات.
Kryo هو إطار عمل لتسهيل التسلسل. لا يفرض إطار العمل نفسه مخططًا أو يهتم بكيفية كتابة البيانات أو قراءتها أو كيفية كتابتها. المُسلسلات قابلة للتوصيل وتتخذ القرارات بشأن ما يجب قراءته وكتابته. يتم توفير العديد من أجهزة التسلسل خارج الصندوق لقراءة البيانات وكتابتها بطرق مختلفة. على الرغم من أن برامج التسلسل المتوفرة يمكنها قراءة وكتابة معظم الكائنات، إلا أنه يمكن بسهولة استبدالها جزئيًا أو كليًا ببرامج التسلسل الخاصة بك.
عندما يقوم Kryo بكتابة مثيل لكائن ما، فقد يحتاج أولاً إلى كتابة شيء يحدد فئة الكائن. افتراضيًا، يجب تسجيل جميع الفئات التي سيقرأها Kryo أو يكتبها مسبقًا. يوفر التسجيل معرف فئة int، والمتسلسل الذي سيتم استخدامه للفئة، ومنشئ الكائن المستخدم لإنشاء مثيلات للفئة.
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class );
Output output = ...
SomeClass object = ...
kryo . writeObject ( output , object );
أثناء إلغاء التسلسل، يجب أن يكون للفئات المسجلة نفس المعرفات التي كانت لديها أثناء عملية التسلسل. عند التسجيل، يتم تعيين معرف العدد الصحيح التالي المتاح للفصل، مما يعني أن تسجيل فئات الطلب أمر مهم. يمكن تحديد معرف الفئة بشكل اختياري بشكل صريح لجعل الطلب غير مهم:
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class , 9 );
kryo . register ( AnotherClass . class , 10 );
kryo . register ( YetAnotherClass . class , 11 );
معرفات الفئة -1 و -2 محجوزة. يتم استخدام معرفات الفئة 0-8 افتراضيًا للأنواع الأولية والسلاسل، على الرغم من إمكانية إعادة استخدام هذه المعرفات. تتم كتابة المعرفات كمتغيرات موجبة محسنة، لذا تكون أكثر كفاءة عندما تكون أعدادًا صحيحة صغيرة وموجبة. لا يتم تسلسل المعرفات السلبية بكفاءة.
تحت الأغطية، يعالج 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.
يتم فرز المُسلسلات الافتراضية بحيث تتم مطابقة الفئات الأكثر تحديدًا أولاً، ولكن تتم مطابقتها بخلاف ذلك بترتيب إضافتها. يمكن أن يكون ترتيب إضافتها ذا صلة بالواجهات.
إذا لم تتطابق أي مُسلسلات افتراضية مع فئة ما، فسيتم استخدام المُسلسل الافتراضي العام. يتم تعيين المُسلسِل الافتراضي العام على FieldSerializer افتراضيًا، ولكن يمكن تغييره. عادةً ما يكون المُسلسِل العام هو الذي يمكنه التعامل مع العديد من الأنواع المختلفة.
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)
التي تسمح له برفض التعامل مع فئة ما، حتى لو كانت تطابق الفئة. يتيح ذلك للمصنع التحقق من وجود واجهات متعددة أو تنفيذ منطق آخر.
في حين أن بعض أدوات التسلسل مخصصة لفئة معينة، إلا أن بعضها الآخر يمكنه إجراء تسلسل للعديد من الفئات المختلفة. يمكن للمتسلسلات استخدام 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 بإنشاء مثيلات لفئة دون التأثير على واجهة برمجة التطبيقات العامة.
DefaultInstantiatorStrategy هي الطريقة الموصى بها لإنشاء الكائنات باستخدام Kryo. يتم تشغيل المُنشئين تمامًا كما يتم تنفيذه باستخدام كود Java. يمكن أيضًا استخدام آليات بديلة خارج اللغة لإنشاء الأشياء. يستخدم Objenesis StdInstantiatorStrategy واجهات برمجة التطبيقات الخاصة بـ JVM لإنشاء مثيل لفئة دون استدعاء أي مُنشئ على الإطلاق. يعد استخدام هذا أمرًا خطيرًا لأن معظم الفئات تتوقع استدعاء منشئيها. قد يؤدي إنشاء الكائن عن طريق تجاوز منشئيه إلى ترك الكائن في حالة غير صالحة أو غير مهيأة. يجب تصميم الفصول ليتم إنشاؤها بهذه الطريقة.
يمكن تكوين 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 ());
}
}
حتى عندما يعرف المُسلسل الفئة المتوقعة لقيمة ما (على سبيل المثال، فئة الحقل)، إذا لم تكن فئة القيمة المحددة نهائية، فيجب على المُسلسِل أن يكتب أولاً معرف الفئة، ثم القيمة. يمكن إجراء تسلسل للفئات النهائية بشكل أكثر كفاءة لأنها غير متعددة الأشكال.
يتم استخدام Kryo isFinal
لتحديد ما إذا كان الفصل نهائيًا. يمكن تجاوز هذه الطريقة لإرجاعها صحيحة حتى بالنسبة للأنواع غير النهائية. على سبيل المثال، إذا كان أحد التطبيقات يستخدم ArrayList على نطاق واسع ولكنه لا يستخدم مطلقًا فئة فرعية من ArrayList، فإن معاملة ArrayList على أنها نهائية قد تسمح لـ FieldSerializer بحفظ 1-2 بايت لكل حقل ArrayList.
يمكن لـ Kryo إجراء تسلسل لإغلاقات Java 8+ التي تنفذ java.io.Serializable، مع بعض التحذيرات. قد تفشل عمليات الإغلاق المتسلسلة على JVM واحد في إلغاء تسلسلها على JVM مختلف.
يتم استخدام Kryo isClosure
لتحديد ما إذا كانت الفئة عبارة عن إغلاق. إذا كان الأمر كذلك، فسيتم استخدام ClosureSerializer.Closure للعثور على تسجيل الفئة بدلاً من فئة الإغلاق. لإجراء تسلسل لعمليات الإغلاق، يجب تسجيل الفئات التالية: ClosureSerializer.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 لقراءة كائن متداخل في read
Serializer، يجب أولاً استدعاء 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 ;
}
لا ينبغي عادةً أن تستخدم المُسلسلات استخدامًا مباشرًا للمُسلسلات الأخرى، وبدلاً من ذلك يجب استخدام أساليب القراءة والكتابة لـ Kryo. يتيح ذلك لـ Kryo تنظيم التسلسل والتعامل مع الميزات مثل المراجع والكائنات الخالية. في بعض الأحيان يعرف المُسلسِل المُسلسل الذي سيتم استخدامه لكائن متداخل. في هذه الحالة، يجب أن يستخدم أساليب القراءة والكتابة الخاصة بـ 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 مكدس الاستدعاءات عند إجراء تسلسل للكائنات المتداخلة. يقلل Kryo من استدعاءات المكدس، ولكن يمكن أن يحدث تجاوز سعة المكدس للرسوم البيانية العميقة للغاية للكائنات. هذه مشكلة شائعة بالنسبة لمعظم مكتبات التسلسل، بما في ذلك مكتبات تسلسل Java المضمنة. يمكن زيادة حجم المكدس باستخدام -Xss
، لكن لاحظ أن هذا ينطبق على جميع سلاسل الرسائل. قد تستخدم أحجام المكدس الكبيرة في JVM التي تحتوي على العديد من الخيوط كمية كبيرة من الذاكرة.
يمكن استخدام Kryo setMaxDepth
لتحديد الحد الأقصى لعمق الرسم البياني للكائن. يمكن أن يمنع هذا البيانات الضارة من التسبب في تجاوز سعة المكدس.
افتراضيًا، لن تتلقى المُسلسلات أبدًا قيمة خالية، وبدلاً من ذلك سيكتب Kryo بايت حسب الحاجة للإشارة إلى قيمة فارغة أو ليست فارغة. إذا كان المُسلسل أكثر كفاءة من خلال التعامل مع القيم الخالية نفسها، فيمكنه استدعاء Serializer setAcceptsNull(true)
. يمكن استخدام هذا أيضًا لتجنب كتابة البايت الذي يشير إلى القيمة الفارغة عندما يكون من المعروف أن جميع المثيلات التي سيتعامل معها المُسلسِل لن تكون فارغة أبدًا.
يوفر Kryo getGenerics
معلومات عامة عن النوع حتى تكون المُسلسلات أكثر كفاءة. يُستخدم هذا بشكل شائع لتجنب كتابة الفصل عندما تكون فئة معلمة النوع نهائية.
يتم تمكين استنتاج النوع العام افتراضيًا ويمكن تعطيله باستخدام Kryo setOptimizedGenerics(false)
. يمكن أن يؤدي تعطيل تحسين الأدوية العامة إلى زيادة الأداء على حساب حجم تسلسلي أكبر.
إذا كان للفئة معلمة نوع واحدة، nextGenericClass
يُرجع فئة معلمة النوع، أو null إذا لم يكن هناك شيء. بعد قراءة أو كتابة أي كائنات متداخلة، يجب استدعاء 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 أولاً لتخزين معلمات النوع لفئة ما. أثناء عملية التسلسل، يتم استدعاء 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
لإنشاء مثيل جديد. من السهل كتابة المُسلسل الخاص بك لتخصيص العملية وطرق الاتصال قبل أو بعد التسلسل وما إلى ذلك.
تدعم برامج التسلسل النسخ فقط في حالة تجاوز copy
. على غرار read
Serializer، تحتوي هذه الطريقة على منطق إنشاء النسخة وتكوينها. تمامًا مثل 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
التسلسل - تنفيذ copy
الافتراضية سيعيد الكائن الأصلي.
يتم تطبيق القواعد الأساسية التالية على ترقيم إصدارات Kryo:
يعد ترقية أي تبعية حدثًا مهمًا ، لكن مكتبة التسلسل أكثر عرضة للكسر من معظم التبعيات. عند ترقية Kryo ، تحقق من اختلافات الإصدار واختبار الإصدار الجديد جيدًا في تطبيقاتك الخاصة. نحاول أن نجعلها آمنة وسهلة قدر الإمكان.
تفترض المسلسلات Kryo المقدمة افتراضيًا أن Java سيتم استخدامه للتخلص من التسلسل ، لذلك لا يحددون بشكل صريح التنسيق المكتوب. يمكن كتابة المسلسلات باستخدام تنسيق موحد يمكن قراءته بسهولة أكبر بواسطة لغات أخرى ، ولكن لا يتم توفير هذا بشكل افتراضي.
بالنسبة لبعض الاحتياجات ، مثل التخزين طويل الأجل للبايت المسلسل ، يمكن أن يكون من المهم كيفية تعامل التسلسل مع التغييرات مع الفصول. يُعرف هذا بالتوافق إلى الأمام (قراءة بايت متسلسلة بواسطة فصول أحدث) والتوافق الخلفي (القراءة بايت المسلسل بواسطة الفصول القديمة). يوفر Kryo بعض المسلسلات العامة التي تتخذ مقاربات مختلفة للتعامل مع التوافق. يمكن تطوير المسلسلات الإضافية بسهولة للتوافق للأمام والخلف ، مثل المسلسل التسلسلي الذي يستخدم مخططًا مكتوبًا خارجيًا.
عندما يتغير الفصل أكثر من تعامله مع المسلسل ، يمكن كتابة المسلسل لنقل البيانات إلى فئة مختلفة. يجب استبدال كل استخدام الفئة القديمة في رمز التطبيق بالفئة الجديدة. يتم الاحتفاظ بالطبقة القديمة فقط لهذا المسلسل.
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 العديد من المسلسلات مع خيارات التكوين المختلفة ومستويات التوافق. يمكن العثور على المسلسلات الإضافية في مشروع شقيقة Kryo-Serializers ، والذي يستضيف المسلسلات التي تصل إلى واجهات برمجة التطبيقات الخاصة أو غير آمنة تمامًا على جميع JVMs. يمكن العثور على المزيد من المسلسلات في قسم الروابط.
يعمل FieldSerializer عن طريق تسلسل كل حقل غير راقص. يمكنه تسلسل pojos والعديد من الفئات الأخرى دون أي تكوين. تتم كتابة جميع الحقول غير العامة وقراءتها افتراضيًا ، لذلك من المهم تقييم كل فئة سيتم تسلسلها. إذا كانت الحقول عامة ، فقد يكون التسلسل أسرع.
FieldSerializer فعال من خلال كتابة بيانات الحقل فقط ، دون أي معلومات عن المخطط ، باستخدام ملفات فئة Java كمخطط. لا يدعم إضافة أو إزالة أو تغيير نوع الحقول دون إبطال البايت المسلسل مسبقًا. لا يُسمح بإعادة تسمية حقول إلا إذا لم يغير الترتيب الأبجدي للحقول.
يمكن أن تكون عيوب توافق FieldSerializer مقبولة في العديد من المواقف ، مثل عند إرسال البيانات عبر الشبكة ، ولكن قد لا تكون خيارًا جيدًا لتخزين البيانات على المدى الطويل لأن فئات Java لا يمكن أن تتطور. في كثير من الحالات ، يعد TaggedFieldSerializer خيارًا أفضل.
جلسة | وصف | القيمة الافتراضية |
---|---|---|
fieldsCanBeNull | عندما يكون من المفترض أن تكون هناك قيم حقل خالية ، والتي يمكن أن توفر 0-1 بايت لكل حقل. | حقيقي |
setFieldsAsAccessible | عندما يكون هذا صحيحًا ، سيتم تسلسل جميع الحقول غير المترتبة (بما في ذلك الحقول الخاصة) setAccessible إذا لزم الأمر. إذا كان خطأ ، سيتم التسلسل فقط الحقول في واجهة برمجة التطبيقات العامة. | حقيقي |
ignoreSyntheticFields | إذا كان ذلك صحيحًا ، يتم تسلسل الحقول الاصطناعية (التي تم إنشاؤها بواسطة المترجم للنطاق). | خطأ شنيع |
fixedFieldTypes | إذا كان ذلك صحيحًا ، فمن المفترض أن يتطابق نوع كل قيمة ملموسة في نوع الحقل مع نوع الحقل. هذا يزيل الحاجة إلى كتابة معرف الفصل لقيم الحقل. | خطأ شنيع |
copyTransient | إذا كان ذلك صحيحًا ، فسيتم نسخ جميع الحقول العابرة. | حقيقي |
serializeTransient | إذا كان ذلك صحيحًا ، فسيتم تسلسل الحقول العابرة. | خطأ شنيع |
variableLengthEncoding | إذا كان صحيحًا ، يتم استخدام قيم الطول المتغير للحقول الداخلية والطويلة. | حقيقي |
extendedFieldNames | إذا كان ذلك صحيحًا ، يتم تسبق أسماء الحقول من خلال فئة الإعلان. هذا يمكن أن يتجنب النزاعات عندما يكون لدى الفئة الفرعية حقل بنفس الاسم مثل فئة فائقة. | خطأ شنيع |
يوفر 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 لاستخدامها لقيمة الحقل. هذا يزيل الحاجة إلى كتابة معرف الفصل للقيمة. إذا كانت فئة قيمة الحقل عبارة عن غلاف بدائي أو بدائي أو نهائي ، فإن هذا الإعداد الافتراضي لفئة الحقل. | باطل |
serializer | يعين المسلسل لاستخدامه لقيمة الحقل. إذا تم تعيين المسلسل ، فإن بعض المسلسلات تتطلب تعيين فئة القيمة أيضًا. إذا كان NULL ، سيتم استخدام المسلسل المسجل لدى Kryo لفئة قيمة الحقل. | باطل |
variableLengthEncoding | إذا كان صحيحًا ، يتم استخدام قيم الطول المتغيرة. هذا ينطبق فقط على int أو الحقول الطويلة. | حقيقي |
optimizePositive | إذا كان صحيحًا ، يتم تحسين القيم الموجبة لقيم الطول المتغيرة. ينطبق هذا فقط على Int أو الحقول الطويلة عند استخدام ترميز الطول المتغير. | حقيقي |
يمكن استخدام التعليقات التوضيحية لتكوين المسلسلات لكل حقل.
تعليق توضيحي | وصف |
---|---|
@Bind | يضبط إعدادات Cachedfield لأي حقل. |
@CollectionBind | يعين إعدادات collectionerializer لحقول التجميع. |
@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: varint إضافي واحد.
جلسة | وصف | القيمة الافتراضية |
---|---|---|
compatible | عندما يكون خطأ ، يتم إلقاء استثناء عند قراءة كائن بإصدار مختلف. إصدار كائن هو الإصدار الأقصى من أي حقل. | حقيقي |
يرث الإصدار FieldSerializer أيضًا جميع إعدادات FieldSerializer.
يمتد TaggedFieldSerializer FieldSerializer لتوفير التوافق المتخلف والتوافق الاختياري للأمام. هذا يعني أنه يمكن إضافة الحقول أو إعادة تسميتها وإزالتها اختياريًا دون إبطال البايتات المسلسلات مسبقًا. تغيير نوع الحقل غير مدعوم.
يتم تسلسل الحقول التي تحتوي على تعليق @Tag(int)
فقط. يجب أن تكون قيم علامة الحقل فريدة من نوعها ، سواء داخل فئة وجميع فئاتها الفائقة. يتم طرح استثناء إذا تمت مواجهة قيم العلامات المكررة.
يعتمد التوافق الأمامي والخلف وأداء التسلسل على إعدادات readUnknownTagData
وإعدادات chunkedEncoding
. بالإضافة إلى ذلك ، تتم كتابة varint قبل كل حقل لقيمة العلامة.
عندما تكون readUnknownTagData
و chunkedEncoding
خاطئة ، يجب ألا تتم إزالة الحقول ولكن يمكن تطبيق التعليق التوضيحي @Deprecated
. تتم قراءة الحقول المهملة عند قراءة البايتات القديمة ولكن لا يتم كتابتها إلى بايت جديدة. يمكن أن تتطور الفصول الدراسية عن طريق قراءة قيم الحقول المهملة وكتابتها في مكان آخر. يمكن إعادة تسمية الحقول و/أو جعلها خاصة لتقليل الفوضى في الفصل (على سبيل المثال ، ignored1
، ignored2
).
TaggedFieldSerializer (مع readUnknownTagData
و chunkedEncoding
false) هو المسلسل المقترح لمعظم الفصول حيث يمكن شرح الحقول. إنها تتيح للتطور والفصول لإزالة الحقول من البيانات المسلسل (عن طريق الإهمال) ، وتلبية احتياجات معظم التطبيقات دون إضافة الكثير إلى الحجم التسلسلي.
جلسة | وصف | القيمة الافتراضية |
---|---|---|
readUnknownTagData | عند مواجهة علامة خاطئة وغير معروفة ، يتم إلقاء استثناء أو ، إذا كان chunkedEncoding صحيحًا ، يتم تخطي البيانات.عندما يكون صحيحًا ، تتم كتابة فئة كل قيمة حقل قبل القيمة. عند مواجهة علامة غير معروفة ، يتم إجراء محاولة لقراءة البيانات. يتم استخدام هذا لتخطي البيانات ، وإذا تم تمكين المراجع ، فإن أي قيم أخرى في الرسم البياني الكائن تشير إلى أنه لا يزال من الممكن إلغاء التخلص من البيانات. إذا فشلت قراءة البيانات (على سبيل المثال ، فإن الفصل غير معروف أو تمت إزالته) ، فسيتم إلقاء استثناء أو ، إذا كان chunkedEncoding صحيحًا ، يتم تخطي البيانات.في كلتا الحالتين ، إذا تم تخطي البيانات وتمكين المراجع ، فلا تتم قراءة أي مراجع في البيانات التي تم تخطيها وقد تتلقى مزيد من التخلص من المراجع الخاطئة والفشل. | خطأ شنيع |
chunkedEncoding | عندما يكون صحيحًا ، تتم كتابة الحقول مع ترميز مكثف للسماح بتخطي بيانات الحقل غير المعروفة. هذا يؤثر على الأداء. | خطأ شنيع |
chunkSize | الحد الأقصى لحجم كل قطعة للترميز المكثف. | 1024 |
يرث TaggedFieldserializer أيضًا جميع إعدادات FieldSerializer.
يمتد CompatibleFieldSerializer إلى FieldSerializer لتوفير التوافق الأمامي والخلف. هذا يعني أنه يمكن إضافة الحقول أو إزالتها دون إبطال البايتات المسلسل مسبقًا. لا يتم دعم إعادة تسمية أو تغيير نوع الحقل. مثل FieldSerializer ، يمكن أن يسلط معظم الفصول دون الحاجة إلى التعليقات التوضيحية.
يعتمد التوافق الأمامي والخلف وأداء التسلسل على إعدادات readUnknownFieldData
وإعدادات chunkedEncoding
. بالإضافة إلى ذلك ، في المرة الأولى التي يتم فيها مواجهة الفصل في البايتات المسلسل ، يتم كتابة مخطط بسيط يحتوي على سلاسل اسم الحقل. نظرًا لأن بيانات الحقل تم تحديدها بالاسم ، إذا كانت الفئة الفائقة تحتوي على حقل بنفس الاسم مثل الفئة الفرعية ، فيجب أن تكون extendedFieldNames
صحيحة.
جلسة | وصف | القيمة الافتراضية |
---|---|---|
readUnknownFieldData | عندما يتم مواجهة حقل كاذب وغير معروف ، يتم طرح استثناء أو ، إذا كان chunkedEncoding صحيحًا ، يتم تخطي البيانات.عندما يكون صحيحًا ، تتم كتابة فئة كل قيمة حقل قبل القيمة. عند مواجهة حقل غير معروف ، يتم إجراء محاولة لقراءة البيانات. يتم استخدام هذا لتخطي البيانات ، وإذا تم تمكين المراجع ، فإن أي قيم أخرى في الرسم البياني الكائن تشير إلى أنه لا يزال من الممكن إلغاء التخلص من البيانات. إذا فشلت قراءة البيانات (على سبيل المثال ، فإن الفصل غير معروف أو تمت إزالته) ، فسيتم إلقاء استثناء أو ، إذا كان chunkedEncoding صحيحًا ، يتم تخطي البيانات.في كلتا الحالتين ، إذا تم تخطي البيانات وتمكين المراجع ، فلا تتم قراءة أي مراجع في البيانات التي تم تخطيها وقد تتلقى مزيد من التخلص من المراجع الخاطئة والفشل. | حقيقي |
chunkedEncoding | عندما يكون صحيحًا ، تتم كتابة الحقول مع ترميز مكثف للسماح بتخطي بيانات الحقل غير المعروفة. هذا يؤثر على الأداء. | خطأ شنيع |
chunkSize | الحد الأقصى لحجم كل قطعة للترميز المكثف. | 1024 |
يرث CompatibleFieldserializer أيضًا جميع إعدادات FieldSerializer.
يشبه Beanserializer إلى حد كبير FieldSerializer ، باستثناء أنه يستخدم طرق Getter و Setter بدلاً من الوصول المباشر إلى الميدان. هذا أبطأ قليلاً ، ولكنه قد يكون أكثر أمانًا لأنه يستخدم واجهة برمجة التطبيقات العامة لتكوين الكائن. مثل FieldSerializer ، فهو لا يوفر توافقًا للأمام أو للخلف.
Collectionerializer تسلسل الكائنات التي تنفذ واجهة java.util.collection.
جلسة | وصف | القيمة الافتراضية |
---|---|---|
elementsCanBeNull | عندما يكون من المفترض أن تكون هناك عناصر في المجموعة خالية ، والتي يمكن أن توفر 0-1 بايت لكل عنصر. | حقيقي |
elementClass | يعين فئة الخرسانة لاستخدامها لكل عنصر في المجموعة. هذا يزيل الحاجة إلى كتابة معرف الفصل لكل عنصر. إذا كانت فئة العناصر معروفة (على سبيل المثال من خلال الأدوية الجيرية) ولفاء بدائي أو بدائي أو نهائي ، فلن يكتب CollectionSerializer معرف الفصل حتى عندما يكون هذا الإعداد لاغيرًا. | باطل |
elementSerializer | يعين المسلسل لاستخدامه لكل عنصر في المجموعة. إذا تم تعيين المسلسل ، فإن بعض المسلسلات تتطلب تعيين فئة القيمة أيضًا. إذا كان NULL ، سيتم استخدام المسلسل المسجل لدى Kryo لفئة كل عنصر. | باطل |
يقوم MapSerializer بتسلسل الكائنات التي تنفذ واجهة java.util.map.
جلسة | وصف | القيمة الافتراضية |
---|---|---|
keysCanBeNull | عندما يكون من المفترض أن تكون هناك مفاتيح في الخريطة خالية ، والتي يمكن أن توفر 0-1 بايت لكل إدخال. | حقيقي |
valuesCanBeNull | عندما يكون من المفترض أن تكون هناك قيم في الخريطة خالية ، والتي يمكن أن توفر 0-1 بايت لكل إدخال. | حقيقي |
keyClass | يعين فئة الخرسانة لاستخدامها في كل مفتاح في الخريطة. هذا يزيل الحاجة إلى كتابة معرف الفصل لكل مفتاح. | باطل |
valueClass | يعين فئة الخرسانة لاستخدامها لكل قيمة في الخريطة. هذا يزيل الحاجة إلى كتابة معرف الفصل لكل قيمة. | باطل |
keySerializer | يعين المسلسل لاستخدامه لكل مفتاح في الخريطة. إذا تم تعيين المسلسل المسلسل ، فإن بعض المسلسلات تتطلب تعيين فئة القيمة أيضًا. إذا كان NULL ، سيتم استخدام المسلسل المسجل لدى Kryo لكل فئة مفتاح. | باطل |
valueSerializer | يعين المسلسل لاستخدامه لكل قيمة في الخريطة. إذا تم تعيين المسلسل الرئيسي ، فإن بعض المسلسلات تتطلب تعيين فئة القيمة أيضًا. إذا كان NULL ، سيتم استخدام المسلسل المسجل لدى Kryo لكل فئة من القيمة. | باطل |
javaserializer و externalizableseriarizer هي مسلسلات Kryo التي تستخدم تسلسل Java المدمج. هذا بطيء مثل تسلسل Java المعتاد ، ولكن قد يكون ضروريًا للدروس القديمة.
Java.io.externalizable و java.io.serializable ليس لديهم مسلسلات افتراضية تم تعيينها افتراضيًا ، لذلك يجب تعيين المسلسلات الافتراضية يدويًا أو تم تعيين المسلسلات عند تسجيل الفصل.
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 من مكتبة تسجيل Minlog الخفيفة الوزن الخفيفة. يمكن تعيين مستوى التسجيل بواسطة إحدى الطرق التالية:
Log . ERROR ();
Log . WARN ();
Log . INFO ();
Log . DEBUG ();
Log . TRACE ();
لا يقوم Kryo بأي تسجيل في INFO
(الافتراضي) وما فوق المستويات. DEBUG
مناسب للاستخدام أثناء التنمية. TRACE
جيد للاستخدام عند تصحيح مشكلة محددة ، ولكن بشكل عام يخرج الكثير من المعلومات التي يمكن تركها.
يدعم Minlog مستوى تسجيل ثابت ، مما يؤدي إلى إزالة مترجم Java لإزالة عبارات التسجيل أدناه هذا المستوى في وقت الترجمة. يجب تجميع Kryo بمستوى التسجيل الثابت في جرة Minlog.
Kryo ليس مؤشرًا آمنًا. يجب أن يكون لكل مؤشر ترابط مثيلات Kryo الخاصة به ، والمدخلات ، والمخرجات.
نظرًا لأن Kryo ليس آمنًا لخيط الخيط ، فقد يتم النظر في حالة كريو وتكوين مثيل Kryo ، في بيئة متعددة الترابط أو التجميع يمكن النظر فيها.
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 فئة البلياردو التي يمكنها تجميع الكريو أو الإدخال أو الإخراج أو مثيلات أي فئة أخرى.
// 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
البلياردو سيحاول إزالة مرجع فارغ إذا تم الوصول إلى الحد الأقصى للسعة.
المعلمة التجمع الثالثة هي الحد الأقصى للسعة. إذا تم تحرير كائن ويحتوي التجمع بالفعل على الحد الأقصى لعدد الكائنات المجانية ، فسيتم إعادة تعيين الكائن المحدد ولكن لم يضاف إلى التجمع. قد يتم حذف السعة القصوى لعدم وجود حد.
إذا قام كائن بتطبيق pool.poolable ، فسيتم استدعاء reset
تجمع عندما يتم تحرير الكائن. هذا يمنح الكائن فرصة لإعادة ضبط حالته لإعادة الاستخدام في المستقبل. بدلاً من ذلك ، يمكن تجاوز reset
تجمع لإعادة ضبط الكائنات. تدخلات وإخراج تنفيذ تجمع لضبط position
total
على 0. Kryo لا تنفذ تجمع لأن حالة الرسم البياني الكائن الخاص بها عادة ما يتم إعادة تعيينها تلقائيًا بعد كل تسلسل (انظر إعادة التعيين). إذا قمت بتعطيل إعادة التعيين التلقائي عبر setAutoReset(false)
، فتأكد من الاتصال بـ Kryo.reset()
قبل إرجاع المثيل إلى التجمع.
تجمع getFree
يعيد عدد الكائنات المتاحة التي يجب الحصول عليها. إذا كان استخدام المراجع اللينة ، فقد يتضمن هذا الرقم كائنات تم جمعها. يمكن استخدام clean
أولاً لإزالة المراجع الناعمة الفارغة.
تجمع getPeak
إرجاع أكبر عدد من الكائنات المجانية على الإطلاق. يمكن أن يساعد ذلك في تحديد ما إذا كان الحد الأقصى لمجموعة التجمع قد تم تعيينه بشكل مناسب. يمكن إعادة تعيينه في أي وقت مع resetPeak
.
يوفر Kryo عددًا من المعايير المستندة إلى JMH وملفات R/GGPLOT2.
يمكن مقارنة Kryo بالعديد من مكتبات التسلسل الأخرى في مشروع JVM Serializers. المعايير صغيرة ، مؤرخة ، ومحلية بدلاً من استخدام JMH ، لذلك أقل جدارة بالثقة. أيضا ، من الصعب للغاية مقارنة المكتبات التسلسلية بدقة باستخدام معيار. تحتوي المكتبات على العديد من الميزات المختلفة وغالبًا ما يكون لها أهداف مختلفة ، لذلك قد تتفوق في حل مشاكل مختلفة تمامًا. لفهم هذه المعايير ، ينبغي تحليل الكود الذي يتم تشغيله والبيانات التي يتم تسلسلها وتناقضها مع احتياجاتك المحددة. يتم تحسين بعض المسلسلات بشكل كبير واستخدام صفحات الكود ، والبعض الآخر يستخدم فقط بضعة أسطر. من الجيد إظهار ما هو ممكن ، ولكن قد لا يكون مقارنة ذات صلة للعديد من المواقف.
هناك عدد من المشاريع باستخدام Kryo. عدد قليل مدرج أدناه. يرجى إرسال طلب سحب إذا كنت ترغب في تضمين مشروعك هنا.