Kryo는 빠르고 효율적인 Java용 바이너리 객체 그래프 직렬화 프레임워크입니다. 프로젝트의 목표는 빠른 속도, 작은 크기, 사용하기 쉬운 API입니다. 이 프로젝트는 파일, 데이터베이스 또는 네트워크를 통해 개체를 유지해야 할 때마다 유용합니다.
Kryo는 자동으로 깊고 얕은 복사/복제를 수행할 수도 있습니다. 이는 객체에서 객체로 직접 복사하는 것이 아니라 객체에서 객체로 직접 복사하는 것입니다.
이 문서는 Kryo 버전 5.x용입니다. 버전 4.x에 대해서는 Wiki를 참조하세요.
질문, 토론, 지원을 원하시면 Kryo 메일링 리스트를 이용하세요. Kryo 이슈 트래커의 사용을 질문, 토론 또는 지원이 아닌 버그 및 개선 사항으로 제한하십시오.
Kryo는 두 종류의 아티팩트/jar를 게시합니다.
Kryo JAR은 릴리스 페이지와 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 팬은 아닙니다. Maven 없이 Kryo를 사용하려면 lib에 있는 종속성 JAR과 함께 클래스 경로에 Kryo JAR을 배치해야 합니다.
소스에서 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에서 데이터를 가져오고 내보내는 것은 입력 및 출력 클래스를 사용하여 수행됩니다. 이러한 클래스는 스레드로부터 안전하지 않습니다.
Output 클래스는 바이트 배열 버퍼에 데이터를 쓰는 OutputStream입니다. 바이트 배열이 필요한 경우 이 버퍼를 직접 가져와 사용할 수 있습니다. 출력에 OutputStream이 제공되면 버퍼가 가득 차면 바이트를 스트림으로 플러시합니다. 그렇지 않으면 출력이 자동으로 버퍼를 늘릴 수 있습니다. 출력에는 기본 형식과 문자열을 바이트에 효율적으로 쓰기 위한 다양한 방법이 있습니다. DataOutputStream, BufferedOutputStream, FilterOutputStream 및 ByteArrayOutputStream과 유사한 기능을 모두 하나의 클래스로 제공합니다.
팁: 출력 및 입력은 ByteArrayOutputStream의 모든 기능을 제공합니다. 출력을 ByteArrayOutputStream으로 플러시할 이유가 거의 없습니다.
출력은 OutputStream에 쓸 때 바이트를 버퍼링하므로 버퍼링된 바이트가 OutputStream에 쓰이도록 쓰기가 완료된 후 flush
또는 close
호출해야 합니다. 출력에 OutputStream이 제공되지 않은 경우 flush
또는 close
호출할 필요가 없습니다. 많은 스트림과 달리 출력 인스턴스는 위치를 설정하거나 새 바이트 배열 또는 스트림을 설정하여 재사용할 수 있습니다.
팁: 출력이 이미 버퍼링되어 있으므로 출력을 BufferedOutputStream으로 플러시할 이유가 없습니다.
인수가 0인 출력 생성자는 초기화되지 않은 출력을 생성합니다. 출력을 사용하려면 먼저 출력 setBuffer
호출해야 합니다.
입력 클래스는 바이트 배열 버퍼에서 데이터를 읽는 InputStream입니다. 바이트 배열에서 읽기를 원하는 경우 이 버퍼를 직접 설정할 수 있습니다. 입력에 InputStream이 제공되면 버퍼의 모든 데이터를 읽었을 때 스트림에서 버퍼를 채웁니다. 입력에는 바이트에서 기본 요소와 문자열을 효율적으로 읽는 다양한 방법이 있습니다. DataInputStream, BufferedInputStream, FilterInputStream 및 ByteArrayInputStream과 유사한 기능을 모두 하나의 클래스로 제공합니다.
팁: 입력은 ByteArrayInputStream의 모든 기능을 제공합니다. ByteArrayInputStream에서 입력을 읽어야 하는 이유는 거의 없습니다.
입력 close
호출되면 입력의 InputStream이 닫힙니다(있는 경우). InputStream에서 읽지 않는 경우 close
호출할 필요가 없습니다. 많은 스트림과 달리 입력 인스턴스는 위치 및 제한을 설정하거나 새 바이트 배열 또는 InputStream을 설정하여 재사용할 수 있습니다.
인수가 0인 입력 생성자는 초기화되지 않은 입력을 생성합니다. 입력을 사용하려면 먼저 입력 setBuffer
호출해야 합니다.
ByteBufferOutput 및 ByteBufferInput 클래스는 바이트 배열이 아닌 ByteBuffer를 사용한다는 점을 제외하면 출력 및 입력과 동일하게 작동합니다.
UnsafeOutput, UnsafeInput, UnsafeByteBufferOutput 및 UnsafeByteBufferInput 클래스는 많은 경우 더 높은 성능을 위해 sun.misc.Unsafe를 사용한다는 점을 제외하면 안전하지 않은 클래스와 정확히 동일하게 작동합니다. 이러한 클래스를 사용하려면 Util.unsafe
가 true여야 합니다.
안전하지 않은 버퍼를 사용할 때의 단점은 직렬화를 수행하는 시스템의 기본 엔디안과 숫자 유형 표현이 직렬화된 데이터에 영향을 미친다는 것입니다. 예를 들어, 데이터가 X86에 기록되고 SPARC에서 읽는 경우 역직렬화가 실패합니다. 또한 안전하지 않은 버퍼로 데이터를 쓴 경우에는 안전하지 않은 버퍼로 읽어야 합니다.
안전하지 않은 버퍼의 가장 큰 성능 차이는 가변 길이 인코딩이 사용되지 않을 때 큰 기본 배열에서 발생합니다. 안전하지 않은 버퍼 또는 특정 필드(FieldSerializer 사용 시)에 대해서만 가변 길이 인코딩을 비활성화할 수 있습니다.
IO 클래스는 가변 길이 int(varint) 및 long(varlong) 값을 읽고 쓰는 메서드를 제공합니다. 이는 각 바이트의 8번째 비트를 사용하여 더 많은 바이트가 뒤에 오는지 나타냄으로써 수행됩니다. 이는 varint가 1-5바이트를 사용하고 varlong이 1-9바이트를 사용함을 의미합니다. 가변 길이 인코딩을 사용하면 비용이 더 많이 들지만 직렬화된 데이터가 훨씬 작아집니다.
가변 길이 값을 쓸 때 값은 양수 값 또는 음수 값과 양수 값 모두에 대해 최적화될 수 있습니다. 예를 들어 양수 값에 최적화되면 0~127은 1바이트, 128~16383은 2바이트 등으로 기록됩니다. 하지만 5바이트에서는 작은 음수가 최악의 경우입니다. 양수에 대해 최적화되지 않은 경우 이러한 범위는 절반으로 이동합니다. 예를 들어 -64~63은 1바이트, 64~8191, -65~-8192는 2바이트 등으로 기록됩니다.
입력 및 출력 버퍼는 고정 크기 또는 가변 길이 값을 읽고 쓰는 방법을 제공합니다. 버퍼가 고정 크기 또는 가변 길이 값이 기록되는지 여부를 결정하도록 허용하는 방법도 있습니다. 이를 통해 직렬화 코드는 고정 크기가 사용되는 경우 출력을 부풀리는 매우 일반적인 값에 가변 길이 인코딩이 사용되도록 하는 동시에 버퍼 구성이 다른 모든 값을 결정할 수 있도록 허용합니다.
방법 | 설명 |
---|---|
writeInt(int) | 4바이트 int를 씁니다. |
writeVarInt(int, 부울) | 1~5바이트 int를 씁니다. |
writeInt(int, 부울) | 4바이트 또는 1~5바이트 int를 씁니다(버퍼가 결정). |
쓰다긴(긴) | 8바이트 길이를 씁니다. |
writeVarLong(긴, 부울) | 1~9바이트 길이를 씁니다. |
writeLong(긴, 부울) | 8바이트 또는 1-9바이트 길이를 씁니다(버퍼가 결정). |
모든 값에 대해 가변 길이 인코딩을 비활성화하려면 writeVarInt
, writeVarLong
, readVarInt
및 readVarLong
메서드를 재정의해야 합니다.
일부 데이터의 길이를 쓴 다음 데이터를 쓰는 것이 유용할 수 있습니다. 데이터의 길이를 미리 알 수 없는 경우 모든 데이터를 버퍼링하여 길이를 결정한 다음 길이를 쓴 다음 데이터를 쓸 수 있습니다. 이를 위해 단일 대형 버퍼를 사용하면 스트리밍이 방지되고 비합리적으로 큰 버퍼가 필요할 수 있으므로 이는 이상적이지 않습니다.
청크 인코딩은 작은 버퍼를 사용하여 이 문제를 해결합니다. 버퍼가 가득 차면 해당 길이가 기록된 다음 데이터가 기록됩니다. 이것은 하나의 데이터 덩어리입니다. 버퍼가 지워지고 더 이상 쓸 데이터가 없을 때까지 계속됩니다. 길이가 0인 청크는 청크의 끝을 나타냅니다.
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에는 객체를 읽고 쓰는 세 가지 방법 세트가 있습니다. 객체의 구체적인 클래스를 알 수 없고 객체가 null일 수 있는 경우:
kryo . writeClassAndObject ( output , object );
Object object = kryo . readClassAndObject ( input );
if ( object instanceof SomeClass ) {
// ...
}
클래스가 알려져 있고 객체가 null일 수 있는 경우:
kryo . writeObjectOrNull ( output , object );
SomeClass object = kryo . readObjectOrNull ( input , SomeClass . class );
클래스가 알려져 있고 객체가 null일 수 없는 경우:
kryo . writeObject ( output , object );
SomeClass object = kryo . readObject ( input , SomeClass . class );
이러한 모든 메서드는 먼저 사용할 적절한 직렬 변환기를 찾은 다음 이를 사용하여 개체를 직렬화하거나 역직렬화합니다. 직렬 변환기는 재귀 직렬화를 위해 이러한 메서드를 호출할 수 있습니다. 동일한 객체에 대한 다중 참조와 순환 참조는 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 );
이 예에서 출력은 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
객체 그래프가 복사된 후 이전 객체와 새 객체의 맵을 얻는 데 사용될 수 있습니다. 지도는 Kryo reset
에 의해 자동으로 지워지므로 Kryo setAutoReset
이 false인 경우에만 유용합니다.
기본적으로 참조는 활성화되어 있지 않습니다. 즉, 개체가 개체 그래프에 여러 번 나타나는 경우 해당 개체는 여러 번 기록되고 여러 개의 다른 개체로 역직렬화됩니다. 참조가 비활성화되면 순환 참조로 인해 직렬화가 실패합니다. 참조는 직렬화를 위한 Kryo setReferences
및 복사를 위한 setCopyReferences
사용하여 활성화 또는 비활성화됩니다.
참조가 활성화되면 개체 그래프에 처음 나타날 때 각 개체 앞에 varint가 기록됩니다. 동일한 개체 그래프 내에서 해당 클래스가 이후에 나타나는 경우에는 varint만 기록됩니다. 역직렬화 후에는 순환 참조를 포함하여 개체 참조가 복원됩니다. 사용 중인 직렬 변환기는 Serializer read
에서 Kryo reference
호출하여 참조를 지원해야 합니다.
읽거나 쓰는 모든 개체를 추적해야 하므로 참조를 활성화하면 성능에 영향을 미칩니다.
내부적으로 ReferenceResolver는 읽거나 쓴 추적 개체를 처리하고 int 참조 ID를 제공합니다. 다양한 구현이 제공됩니다:
ReferenceResolver useReferences(Class)
재정의될 수 있습니다. 클래스에 대한 참조가 지원되는지 결정하기 위해 부울을 반환합니다. 클래스가 참조를 지원하지 않는 경우 varint 참조 ID는 해당 유형의 개체 앞에 기록되지 않습니다. 클래스에 참조가 필요하지 않고 해당 유형의 개체가 개체 그래프에 여러 번 나타나는 경우 해당 클래스에 대한 참조를 비활성화하면 직렬화된 크기를 크게 줄일 수 있습니다. 기본 참조 확인자는 모든 기본 래퍼 및 열거형에 대해 false를 반환합니다. 직렬화되는 객체 그래프에 따라 String 및 기타 클래스에 대해서도 false를 반환하는 것이 일반적입니다.
public boolean useReferences ( Class type ) {
return ! Util . isWrapperClass ( type ) && ! Util . isEnum ( type ) && type != String . class ;
}
참조 확인자는 단일 개체 그래프의 최대 참조 수를 결정합니다. Java 배열 인덱스는 Integer.MAX_VALUE
로 제한되므로 배열 기반 데이터 구조를 사용하는 참조 확인자는 20억 개가 넘는 개체를 직렬화할 때 java.lang.NegativeArraySizeException
이 발생할 수 있습니다. Kryo는 int 클래스 ID를 사용하므로 단일 개체 그래프의 최대 참조 수는 int의 양수 및 음수의 전체 범위(~40억)로 제한됩니다.
Kryo getContext
사용자 데이터를 저장하기 위한 맵을 반환합니다. Kryo 인스턴스는 모든 직렬 변환기에서 사용할 수 있으므로 이 데이터는 모든 직렬 변환기에서 쉽게 액세스할 수 있습니다.
Kryo getGraphContext
는 유사하지만 각 객체 그래프가 직렬화되거나 역직렬화된 후에 지워집니다. 이를 통해 현재 개체 그래프에만 관련된 상태를 쉽게 관리할 수 있습니다. 예를 들어, 객체 그래프에서 클래스가 처음 발견될 때 일부 스키마 데이터를 작성하는 데 사용할 수 있습니다. 예제는 CompatibleFieldSerializer를 참조하세요.
기본적으로 Kryo reset
각 전체 개체 그래프가 직렬화된 후에 호출됩니다. 이는 클래스 해석기에서 등록되지 않은 클래스 이름, 참조 해석기에서 이전에 직렬화되거나 역직렬화된 객체에 대한 참조를 재설정하고 그래프 컨텍스트를 지웁니다. Kryo setAutoReset(false)
사용하면 자동으로 reset
호출을 비활성화하여 해당 상태가 여러 객체 그래프에 걸쳐 있도록 할 수 있습니다.
Kryo는 직렬화를 용이하게 하는 프레임워크입니다. 프레임워크 자체는 스키마를 적용하지 않으며 데이터를 쓰거나 읽는 내용이나 방법에 관심을 두지 않습니다. 직렬 변환기는 플러그형이며 무엇을 읽고 쓸지 결정합니다. 다양한 방법으로 데이터를 읽고 쓸 수 있도록 많은 직렬 변환기가 즉시 제공됩니다. 제공된 직렬 변환기는 대부분의 개체를 읽고 쓸 수 있지만 자체 직렬 변환기로 부분적으로 또는 완전히 쉽게 대체할 수 있습니다.
Kryo가 객체의 인스턴스를 작성할 때 먼저 객체의 클래스를 식별하는 무언가를 작성해야 할 수도 있습니다. 기본적으로 Kryo가 읽거나 쓸 모든 클래스는 미리 등록되어 있어야 합니다. 등록은 int 클래스 ID, 클래스에 사용할 직렬 변환기 및 클래스 인스턴스를 만드는 데 사용되는 개체 인스턴스화기를 제공합니다.
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 );
클래스 ID -1과 -2는 예약되어 있습니다. 클래스 ID 0-8은 기본 유형 및 문자열에 기본적으로 사용되지만 이러한 ID는 다른 용도로 사용될 수 있습니다. ID는 양의 최적화된 변형으로 작성되므로 작은 양의 정수일 때 가장 효율적입니다. 부정적인 ID는 효율적으로 직렬화되지 않습니다.
내부적으로는 ClassResolver가 실제로 바이트 읽기 및 쓰기를 처리하여 클래스를 나타냅니다. 대부분의 경우 기본 구현으로 충분하지만 클래스가 등록될 때 발생하는 작업, 직렬화 중에 등록되지 않은 클래스가 발견될 때 발생하는 작업, 클래스를 나타내기 위해 읽고 쓰는 작업을 사용자 정의하기 위해 이를 대체할 수 있습니다.
Kryo는 클래스를 미리 등록하지 않고도 직렬화를 허용하도록 구성할 수 있습니다.
Kryo kryo = new Kryo ();
kryo . setRegistrationRequired ( false );
Output output = ...
SomeClass object = ...
kryo . writeObject ( output , object );
등록된 클래스와 등록되지 않은 클래스를 혼합하여 사용할 수 있습니다. 등록되지 않은 클래스에는 두 가지 주요 단점이 있습니다.
Kryo를 복사 용도로만 사용하는 경우 등록이 안전하게 비활성화될 수 있습니다.
등록이 필요하지 않은 경우 Kryo setWarnUnregisteredClasses
활성화하여 등록되지 않은 클래스가 발견될 때 메시지를 기록할 수 있습니다. 이는 등록되지 않은 모든 클래스 목록을 쉽게 얻는 데 사용할 수 있습니다. Kryo unregisteredClassMessage
재정의하여 로그 메시지를 사용자 정의하거나 다른 작업을 수행할 수 있습니다.
클래스가 등록되면 선택적으로 serializer 인스턴스를 지정할 수 있습니다. 역직렬화 중에 등록된 클래스는 직렬화 중에 가지고 있던 것과 정확히 동일한 직렬 변환기 및 직렬 변환기 구성을 가져야 합니다.
Kryo kryo = new Kryo ();
kryo . register ( SomeClass . class , new SomeSerializer ());
kryo . register ( AnotherClass . class , new AnotherSerializer ());
직렬 변환기가 지정되지 않거나 등록되지 않은 클래스가 발견되면 클래스를 직렬 변환기에 매핑하는 "기본 직렬 변환기" 목록에서 직렬 변환기가 자동으로 선택됩니다. 기본 직렬 변환기가 많아도 직렬화 성능에 영향을 주지 않으므로 기본적으로 Kryo에는 다양한 JRE 클래스에 대한 기본 직렬 변환기가 50개 이상 있습니다. 추가 기본 직렬 변환기를 추가할 수 있습니다.
Kryo kryo = new Kryo ();
kryo . setRegistrationRequired ( false );
kryo . addDefaultSerializer ( SomeClass . class , SomeSerializer . class );
Output output = ...
SomeClass object = ...
kryo . writeObject ( output , object );
이로 인해 SomeClass 또는 SomeClass를 확장하거나 구현하는 클래스가 등록될 때 SomeSerializer 인스턴스가 생성됩니다.
기본 직렬 변환기는 더 구체적인 클래스가 먼저 일치되도록 정렬되지만 그렇지 않으면 추가된 순서대로 일치됩니다. 추가되는 순서는 인터페이스와 관련될 수 있습니다.
클래스와 일치하는 기본 직렬 변환기가 없으면 전역 기본 직렬 변환기가 사용됩니다. 전역 기본 직렬 변환기는 기본적으로 FieldSerializer로 설정되어 있지만 변경할 수 있습니다. 일반적으로 전역 직렬 변환기는 다양한 유형을 처리할 수 있는 것입니다.
Kryo kryo = new Kryo ();
kryo . setDefaultSerializer ( TaggedFieldSerializer . class );
kryo . register ( SomeClass . class );
이 코드를 사용하면 SomeClass와 일치하는 기본 직렬 변환기가 없다고 가정하고 TaggedFieldSerializer가 사용됩니다.
클래스는 Kryo의 기본 직렬 변환기 중 하나를 선택하는 대신 사용되는 DefaultSerializer 주석을 사용할 수도 있습니다.
@ DefaultSerializer ( SomeClassSerializer . class )
public class SomeClass {
// ...
}
유연성을 극대화하기 위해 Kryo getDefaultSerializer
재정의하여 직렬 변환기를 선택하고 인스턴스화하기 위한 사용자 정의 논리를 구현할 수 있습니다.
addDefaultSerializer(Class, Class)
메소드는 직렬 변환기 구성을 허용하지 않습니다. Serializer 클래스 대신 Serializer 팩토리를 설정하여 팩토리에서 각 Serializer 인스턴스를 생성하고 구성할 수 있습니다. 공통 직렬 변환기에 대해 팩토리가 제공되며, 종종 생성된 직렬 변환기를 구성하기 위한 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 );
Serializer 팩토리에는 클래스와 일치하는 경우에도 클래스 처리를 거부할 수 있는 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는 인수가 없는 생성자를 호출하기 위해 ReflectASM을 사용하여 객체를 생성하는 DefaultInstantiatorStrategy를 제공합니다. 이것이 가능하지 않은 경우 리플렉션을 사용하여 인수가 없는 생성자를 호출합니다. 이것도 실패하면 예외가 발생하거나 대체 InstantiatorStrategy를 시도합니다. Reflection은 setAccessible
사용하므로 인수가 0인 비공개 생성자는 Kryo가 공개 API에 영향을 주지 않고 클래스의 인스턴스를 생성할 수 있도록 하는 좋은 방법이 될 수 있습니다.
DefaultInstantiatorStrategy는 Kryo로 객체를 생성하는 데 권장되는 방법입니다. Java 코드에서 수행되는 것과 마찬가지로 생성자를 실행합니다. 대안적인 언어 외 메커니즘을 사용하여 객체를 생성할 수도 있습니다. Objenesis StdInstantiatorStrategy는 JVM 특정 API를 사용하여 생성자를 전혀 호출하지 않고 클래스의 인스턴스를 생성합니다. 대부분의 클래스는 생성자가 호출될 것으로 예상하므로 이를 사용하는 것은 위험합니다. 생성자를 우회하여 객체를 생성하면 객체가 초기화되지 않거나 잘못된 상태가 될 수 있습니다. 클래스는 이러한 방식으로 생성되도록 설계되어야 합니다.
Kryo는 먼저 DefaultInstantiatorStrategy를 시도한 다음 필요한 경우 StdInstantiatorStrategy로 폴백하도록 구성할 수 있습니다.
kryo . setInstantiatorStrategy ( new DefaultInstantiatorStrategy ( new StdInstantiatorStrategy ()));
또 다른 옵션은 Java의 내장 직렬화 메커니즘을 사용하여 인스턴스를 생성하는 SerializingInstantiatorStrategy입니다. 이를 사용하여 클래스는 java.io.Serialized를 구현해야 하며 슈퍼 클래스의 첫 번째 인수가 없는 생성자가 호출됩니다. 이는 또한 생성자를 우회하므로 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 );
}
});
일부 직렬 변환기는 적절한 시점에 create
에 필요한 데이터를 쓰기 위해 재정의할 수 있는 writeHeader
메서드를 제공합니다.
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
은 클래스가 최종인지 확인하는 데 사용됩니다. 최종 유형이 아닌 경우에도 true를 반환하도록 이 메서드를 재정의할 수 있습니다. 예를 들어 응용 프로그램이 ArrayList를 광범위하게 사용하지만 ArrayList 하위 클래스를 전혀 사용하지 않는 경우 ArrayList를 최종 클래스로 처리하면 FieldSerializer가 ArrayList 필드당 1-2바이트를 절약할 수 있습니다.
Kryo는 몇 가지 주의사항과 함께 java.io.Serialized를 구현하는 Java 8+ 클로저를 직렬화할 수 있습니다. 하나의 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 );
직렬화 가능을 구현하지 않은 클로저를 직렬화하는 것은 약간의 노력으로 가능합니다.
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
에서 중첩된 개체를 읽는 데 사용되는 경우 중첩된 개체가 상위 개체를 참조하는 것이 가능하다면 먼저 상위 개체와 함께 Kryo reference
호출해야 합니다. 중첩된 개체가 상위 개체를 참조할 수 없는 경우, 중첩된 개체에 Kryo가 사용되지 않는 경우 또는 참조가 사용되지 않는 경우 Kryo reference
호출할 필요가 없습니다. 중첩된 개체가 동일한 직렬 변환기를 사용할 수 있는 경우 직렬 변환기는 재진입 가능해야 합니다.
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는 직렬화를 조정하고 참조 및 null 개체와 같은 기능을 처리할 수 있습니다. 때때로 직렬 변환기는 중첩된 개체에 사용할 직렬 변환기를 알고 있습니다. 이 경우 직렬 변환기를 허용하는 Kryo의 읽기 및 쓰기 메서드를 사용해야 합니다.
객체가 null일 수 있는 경우:
Serializer serializer = ...
kryo . writeObjectOrNull ( output , object , serializer );
SomeClass object = kryo . readObjectOrNull ( input , SomeClass . class , serializer );
객체가 null일 수 없는 경우:
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
객체 그래프의 최대 깊이를 제한하는 데 사용할 수 있습니다. 이렇게 하면 악성 데이터로 인해 스택 오버플로가 발생하는 것을 방지할 수 있습니다.
기본적으로 직렬 변환기는 null을 수신하지 않습니다. 대신 Kryo는 null 또는 null이 아님을 표시하기 위해 필요에 따라 바이트를 씁니다. null 자체를 처리하여 serializer가 더 효율적일 수 있는 경우 Serializer setAcceptsNull(true)
호출할 수 있습니다. 이는 또한 직렬 변환기가 처리할 모든 인스턴스가 결코 null이 아닐 것으로 알려진 경우 null 표시 바이트를 쓰는 것을 방지하는 데 사용될 수 있습니다.
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 인스턴스의 배열을 반환하고 각 GenericType에 대한 클래스를 가져오는 데 resolve
사용됩니다. 중첩된 객체를 읽거나 쓴 후에는 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 ;
}
}
직렬 변환기를 사용하는 대신 클래스는 KryoSerialized(java.io.Externalized와 유사)를 구현하여 자체 직렬화를 수행하도록 선택할 수 있습니다.
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
호출되기 전에 이미 생성되어야 하므로 클래스는 자체 생성을 제어할 수 없습니다. KryoSerialized 클래스는 Kryo newInstance
사용하여 새 인스턴스를 생성하는 기본 직렬 변환기 KryoSerializedSerializer를 사용합니다. 프로세스를 사용자 정의하고 직렬화 전후에 메서드를 호출하는 등의 작업을 수행하기 위해 자신만의 직렬 변환기를 작성하는 것은 쉽습니다.
직렬 변환기는 copy
재정의된 경우에만 복사를 지원합니다. Serializer read
와 유사하게 이 메서드에는 복사본을 생성하고 구성하는 논리가 포함되어 있습니다. read
와 마찬가지로, 하위 개체 중 하나라도 상위 개체를 참조할 수 있는 경우 Kryo를 사용하여 하위 개체를 복사하기 전에 Kryo reference
호출해야 합니다.
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)
사용할 수 있습니다. 이 경우 Serializer 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 Sister Project에서 찾을 수 있으며,이 프로젝트는 개인 API에 액세스하거나 모든 JVM에서 완벽하게 안전하지 않은 시리얼 라이저를 호스팅합니다. 더 많은 직렬화기는 링크 섹션에서 찾을 수 있습니다.
FieldSerializer는 각 비 유도 필드를 직렬화하여 작동합니다. 구성없이 Pojos 및 기타 많은 클래스를 일련의 클래스를 일련화 할 수 있습니다. 모든 비공개 필드는 기본적으로 작성 및 읽기 때문에 직렬화 될 각 클래스를 평가하는 것이 중요합니다. 필드가 공개되면 직렬화가 더 빠를 수 있습니다.
FieldSerializer는 스키마 정보없이 필드 데이터 만 스키마로 사용하여 필드 데이터 만 작성하여 효율적입니다. 이전에 직렬화 된 바이트를 무효화하지 않고 필드 유형을 추가, 제거 또는 변경하는 것은 지원하지 않습니다. 필드의 이름 변경은 필드의 알파벳 순서를 변경하지 않는 경우에만 허용됩니다.
FieldSerializer의 호환성 단점은 네트워크를 통해 데이터를 보낼 때와 같은 많은 상황에서 허용 될 수 있지만 Java 클래스가 진화 할 수 없기 때문에 장기 데이터 저장에 적합하지 않을 수 있습니다. 대부분의 경우 TaggedFieldSerializer가 더 나은 선택입니다.
환경 | 설명 | 기본값 |
---|---|---|
fieldsCanBeNull | false 일 때 필드 값이 null이 없으므로 필드 당 0-1 바이트를 절약 할 수 있습니다. | 진실 |
setFieldsAsAccessible | 사실 인 경우, 모든 비 초도 필드 (개인 필드 포함)는 필요한 경우 직렬화되고 setAccessible . False 인 경우 공개 API의 필드 만 직렬화됩니다. | 진실 |
ignoreSyntheticFields | 사실이라면 합성 필드 (범위를 위해 컴파일러에 의해 생성)가 직렬화됩니다. | 거짓 |
fixedFieldTypes | 사실이라면 모든 필드 값의 콘크리트 유형이 필드 유형과 일치한다고 가정합니다. 이는 필드 값에 대한 클래스 ID를 작성해야합니다. | 거짓 |
copyTransient | 사실이라면 모든 과도 필드가 복사됩니다. | 진실 |
serializeTransient | 사실이라면 과도 필드가 직렬화됩니다. | 거짓 |
variableLengthEncoding | true 인 경우 가변 길이 값은 int 및 긴 필드에 사용됩니다. | 진실 |
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 | false 일 때 필드 값이 결코 null이 아니라고 가정하면 0-1 바이트를 절약 할 수 있습니다. | 진실 |
valueClass | 현장 값에 사용할 콘크리트 클래스 및 직렬 라이저를 설정합니다. 이렇게하면 값에 대한 클래스 ID를 작성해야합니다. 필드 값 클래스가 원시적, 원시적 래퍼 또는 최종 인 경우이 설정은 필드 클래스로 기본적으로 표시됩니다. | null |
serializer | 필드 값에 사용할 직렬 라이저를 설정합니다. 시리얼 라이저가 설정되면 일부 시리얼 라이저는 값 클래스도 설정해야했습니다. NULL 인 경우 Field Value 클래스에 Kryo에 등록 된 시리얼 라이저가 사용됩니다. | null |
variableLengthEncoding | true 인 경우 가변 길이 값이 사용됩니다. 이것은 int 또는 긴 필드에만 적용됩니다. | 진실 |
optimizePositive | true 인 경우 가변 길이 값에 대해 양수 값이 최적화됩니다. 이는 가변 길이 인코딩을 사용하는 경우 int 또는 긴 필드에만 적용됩니다. | 진실 |
주석은 각 필드의 직렬화를 구성하는 데 사용될 수 있습니다.
주석 | 설명 |
---|---|
@Bind | 모든 필드의 캐시 필드 설정을 설정합니다. |
@CollectionBind | 수집 필드에 대한 수집 서식기 설정을 설정합니다. |
@MapBind | 맵 필드의 맵 서리 라이저 설정을 설정합니다. |
@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
이 False 인 경우 필드를 제거해서는 안되지만 @Deprecated
주석을 적용 할 수 있습니다. 더 이상 사용되지 않은 필드는 오래된 바이트를 읽을 때 읽지 만 새 바이트에 기록되지 않았습니다. 더 이상 사용되지 않은 필드의 값을 읽고 다른 곳에 글을 쓰면 수업이 발전 할 수 있습니다. 필드의 이름을 바꾸고/또는 비공개로 만들 수 있으며 클래스의 혼란을 줄일 수 있습니다 (예 : ignored1
, ignored2
).
taggedfieldserializer ( readUnknownTagData
및 chunkedEncoding
false)는 필드를 주석을 달 수있는 대부분의 클래스에 대한 제안 된 직렬 라이저입니다. 이를 통해 클래스가 진화 할 수 있으며 직렬화 된 데이터 (감가 상각을 통해)에서 필드를 제거하여 직렬화 된 크기에 많이 추가하지 않고 대부분의 응용 프로그램의 요구를 충족시킵니다.
환경 | 설명 | 기본값 |
---|---|---|
readUnknownTagData | False와 알 수없는 태그가 발생하면 예외가 발생하거나 chunkedEncoding 이 사실이면 데이터가 건너 뜁니다.사실이라면 각 필드 값에 대한 클래스는 값 앞에 기록됩니다. 알 수없는 태그가 발생하면 데이터를 읽으려고 시도합니다. 이것은 데이터를 건너 뛰는 데 사용되며, 참조가 활성화되면 데이터가 여전히 사막화 될 수있는 객체 그래프의 다른 값. 데이터를 읽는 경우 (예 : 클래스가 알 수 없거나 제거 된 경우) 예외가 발생하거나 chunkedEncoding 이 사실이면 데이터가 건너 뜁니다.두 경우 모두 데이터를 건너 뛰고 참조가 활성화되면 건너 뛸 수있는 데이터의 참조가 읽지 않으며 추가 사막화는 잘못된 참조를 받고 실패 할 수 있습니다. | 거짓 |
chunkedEncoding | True 일 때, 필드 데이터를 건너 뛸 수 있도록 필드는 청크 인코딩으로 작성됩니다. 이것은 성능에 영향을 미칩니다. | 거짓 |
chunkSize | 청크 인코딩에 대한 각 청크의 최대 크기. | 1024 |
TaggedFieldSerializer는 또한 FieldSerializer의 모든 설정을 상속합니다.
CompatibleFieldSerializer는 FieldSerializer를 전방 및 후진 호환성을 모두 제공하도록 확장합니다. 이는 이전에 직렬화 된 바이트를 무효화하지 않고 필드를 추가하거나 제거 할 수 있음을 의미합니다. 필드 유형을 바꾸거나 변경하는 것은 지원되지 않습니다. FieldSerializer와 마찬가지로 주석이 없어도 대부분의 클래스를 직렬화 할 수 있습니다.
앞뒤로 호환성 및 직렬화 성능은 readUnknownFieldData
및 chunkedEncoding
설정에 따라 다릅니다. 또한, 클래스가 처음으로 직렬화 된 바이트에서 발생하면 필드 이름 문자열이 포함 된 간단한 스키마가 작성됩니다. 필드 데이터가 이름으로 식별되므로 슈퍼 클래스에 서브 클래스와 동일한 필드가있는 경우 extendedFieldNames
참 이루야합니다.
환경 | 설명 | 기본값 |
---|---|---|
readUnknownFieldData | False와 알 수없는 필드가 발생하면 예외가 발생하거나 chunkedEncoding 이 사실이면 데이터가 건너 뜁니다.사실이라면 각 필드 값에 대한 클래스는 값 앞에 기록됩니다. 알 수없는 필드가 발생하면 데이터를 읽으려고 시도합니다. 이것은 데이터를 건너 뛰는 데 사용되며, 참조가 활성화되면 데이터가 여전히 사막화 될 수있는 객체 그래프의 다른 값. 데이터를 읽는 경우 (예 : 클래스가 알 수 없거나 제거 된 경우) 예외가 발생하거나 chunkedEncoding 이 사실이면 데이터가 건너 뜁니다.두 경우 모두 데이터를 건너 뛰고 참조가 활성화되면 건너 뛸 수있는 데이터의 참조가 읽지 않으며 추가 사막화는 잘못된 참조를 받고 실패 할 수 있습니다. | 진실 |
chunkedEncoding | True 일 때, 필드 데이터를 건너 뛸 수 있도록 필드는 청크 인코딩으로 작성됩니다. 이것은 성능에 영향을 미칩니다. | 거짓 |
chunkSize | 청크 인코딩에 대한 각 청크의 최대 크기. | 1024 |
CompatibleFieldSerializer는 또한 FieldSerializer의 모든 설정을 상속합니다.
Beanserializer는 Fieldserializer와 매우 유사합니다. 직접 필드 액세스 대신 Bean Getter 및 Setter 메소드를 사용하는 것을 제외하고. 이것은 약간 느리지 만 공개 API를 사용하여 객체를 구성하기 때문에 더 안전 할 수 있습니다. FieldSerializer와 마찬가지로 전방 또는 후진 호환성을 제공하지 않습니다.
Collectionserializer는 java.util.collection 인터페이스를 구현하는 객체를 직렬화합니다.
환경 | 설명 | 기본값 |
---|---|---|
elementsCanBeNull | false 일 때 컬렉션의 요소가 NULL이 없다고 가정하여 요소 당 0-1 바이트를 절약 할 수 있습니다. | 진실 |
elementClass | 컬렉션의 각 요소에 사용할 콘크리트 클래스를 설정합니다. 이렇게하면 각 요소에 대한 클래스 ID를 작성해야합니다. 요소 클래스가 알려져있는 경우 (예 : 제네릭을 통해) 원시, 원시 래퍼 또는 최종 인 경우이 설정이 NULL이라도 CollectionSerializer가 클래스 ID를 작성하지 않습니다. | null |
elementSerializer | 컬렉션의 모든 요소에 사용하도록 시리얼 라이저를 설정합니다. 시리얼 라이저가 설정되면 일부 시리얼 라이저는 값 클래스도 설정해야했습니다. NULL 인 경우 각 요소 클래스에 대해 Kryo에 등록 된 직렬 라이저가 사용됩니다. | null |
Mapserializer는 java.util.map 인터페이스를 구현하는 객체를 직렬화합니다.
환경 | 설명 | 기본값 |
---|---|---|
keysCanBeNull | False 일 때는 맵의 키가 null이 없으므로 항목 당 0-1 바이트를 절약 할 수 있다고 가정합니다. | 진실 |
valuesCanBeNull | False 일 때는 맵의 값이 NULL이 없으므로 항목 당 0-1 바이트를 절약 할 수 있다고 가정합니다. | 진실 |
keyClass | 지도의 모든 키에 사용할 콘크리트 클래스를 설정합니다. 이렇게하면 각 키에 대한 클래스 ID를 작성해야합니다. | null |
valueClass | 지도의 모든 값에 사용할 콘크리트 클래스를 설정합니다. 이렇게하면 각 값에 대한 클래스 ID를 작성해야합니다. | null |
keySerializer | 맵의 모든 키에 사용하도록 시리얼 라이저를 설정합니다. 값 시리얼 라이저가 설정되면 일부 시리얼 라이저는 값 클래스도 설정해야했습니다. NULL 인 경우 각 키 클래스에 대해 Kryo에 등록 된 직렬 라이저가 사용됩니다. | null |
valueSerializer | 맵의 모든 값에 사용하도록 시리얼 라이저를 설정합니다. 키 시리얼 라이저가 설정되면 일부 시리얼 라이저는 값 클래스도 설정해야했습니다. NULL 인 경우 각 값 클래스에 대해 Kryo에 등록 된 직렬 라이저가 사용됩니다. | null |
JavaSerializer 및 외부화 보관소는 Java의 내장 직렬화를 사용하는 Kryo Serializers입니다. 이것은 평소와 같은 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 Jar로 컴파일해야합니다.
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는 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
객체가 수집 된 모든 부드러운 참조를 제거합니다. 이렇게하면 최대 용량이 설정되지 않았을 때 풀의 크기를 줄일 수 있습니다. 수영장에 최대 용량이 있으면 풀 free
최대 용량에 도달하면 빈 참조를 제거하려고하므로 clean
전화 할 필요가 없습니다.
세 번째 풀 매개 변수는 최대 용량입니다. 객체가 해제되고 풀에 이미 자유 개체의 최대 수가 포함 된 경우 지정된 객체는 재설정되지만 풀에 추가되지 않습니다. 최대 용량은 제한없이 생략 될 수 있습니다.
객체가 풀을 구현하면 풀 가능하면 객체가 해제 될 때 풀 가능한 reset
이 호출됩니다. 이것은 객체에 미래에 재사용을 위해 상태를 재설정 할 수있는 기회를 제공합니다. 또는 객체를 재설정하기 위해 풀 reset
재정의 할 수 있습니다. 입력 및 출력 구현 풀이 가능하게 position
및 total
0으로 설정하십시오. Kryo는 객체 그래프 상태가 일반적으로 각 직렬화 후 자동으로 재설정되기 때문에 풀 가능하지 않습니다 (재설정 참조). setAutoReset(false)
통해 자동 재설정을 비활성화하는 경우 인스턴스를 풀로 되돌리기 전에 Kryo.reset()
에게 전화하십시오.
수영장 getFree
얻을 수있는 객체 수를 반환합니다. 소프트 참조를 사용하는 경우이 숫자에는 수집 된 쓰레기가 포함 된 물체가 포함될 수 있습니다. 빈 소프트 레퍼런스를 제거하는 데 먼저 clean
사용할 수 있습니다.
수영장 getPeak
가장 많은 수의 무료 개체를 반환합니다. 이는 풀의 최대 용량이 적절하게 설정되어 있는지 확인하는 데 도움이 될 수 있습니다. resetPeak
로 언제든지 재설정 할 수 있습니다.
Kryo는 여러 JMH 기반 벤치 마크 및 R/GGPLOT2 파일을 제공합니다.
Kryo는 JVM Serializers 프로젝트의 다른 많은 직렬화 라이브러리와 비교할 수 있습니다. 벤치 마크는 JMH를 사용하기보다는 작고 날짜가 작고 자체적으로 재배되므로 신뢰할 수 없습니다. 또한 벤치 마크를 사용하여 직렬화 라이브러리를 철저히 비교하는 것은 매우 어렵습니다. 라이브러리는 다양한 기능을 가지고 있으며 종종 다른 목표를 가지고 있으므로 완전히 다른 문제를 해결하는 데 탁월합니다. 이러한 벤치 마크를 이해하려면 실행중인 코드와 직렬화되는 데이터를 분석하고 특정 요구와 대조해야합니다. 일부 시리얼 라이저는 매우 최적화되어 있으며 코드의 페이지를 사용하고 일부는 몇 줄만 사용합니다. 이것은 가능한 것이 무엇인지 보여주는 것이 좋지만 많은 상황에서는 관련성이 아닐 수도 있습니다.
Kryo를 사용하는 많은 프로젝트가 있습니다. 일부는 아래에 나열되어 있습니다. 여기에 포함 된 프로젝트를 원하시면 풀 요청을 제출하십시오.