This is a feature release that hardens deserialization against corrupt or malicious input, adds long-requested serializers for unmodifiable and synchronized JDK collections, and fixes several bugs. It also improves compatibility with recent JDKs up to Java 26.
Highlights
Safer deserialization of untrusted or corrupt data (#1284)
Before this release, a tiny corrupt payload that declared a huge array, string, collection or map size could make Kryo allocate gigabytes of memory and fail with an OutOfMemoryError. Kryo now checks declared sizes against the bytes left in the buffer before it allocates anything, and rejects impossible array and string sizes with a KryoException. Collection and map capacities are capped at the bytes remaining. When reading from an InputStream, where the total size isn't known in advance, you can cap declared sizes with the new Input.setMaxArraySize(int):
input.setMaxArraySize(1024 * 1024); // reject any declared array/string/collection/map size above 1M elementsSerializers for unmodifiable and synchronized collections (#1154)
Kryo now includes serializers for the JDK's Collections.unmodifiable* and Collections.synchronized* wrappers. This has been one of the most requested features. Because they read private JDK fields through sun.misc.Unsafe, they are not registered by default and must be enabled explicitly:
UnmodifiableCollectionSerializers.addDefaultSerializers(kryo);
SynchronizedCollectionSerializers.addDefaultSerializers(kryo);
// or, when registration is required:
UnmodifiableCollectionSerializers.registerSerializers(kryo);
SynchronizedCollectionSerializers.registerSerializers(kryo);See the README for details.
All changes
- #1154 Add serializers for unmodifiable and synchronized collections
- #1179 Add better feedback when using abstract classes in serialization (thanks @pvlov)
- #1183 Add
ConcurrentHashMap.KeySetViewserializer (thanks @bergander) - #1253 Make
@Optionalannotation repeatable (thanks @www84) - #1271 Use constants for array base offsets instead of
Unsafe.arrayBaseOffsetto avoid warnings on JDK 25+ - #1283 Fix generics stack leak that poisoned reused Kryo instances (thanks @Icesource)
- #1284 Guard deserialization against unbounded allocation from declared sizes (thanks @joszamama)
- #1300 Cache the Java serializer lookup per type in
ExternalizableSerializer(thanks @vpaturet) - #1309 Preserve circular references in externalizable objects (thanks @Ishubhammohole)
- Revert wrapping of
OutOfMemoryErrorinKryoExceptioninFieldSerializer(#829) - Log a warning when
@BindCollectionor@BindMapsets a serializer without the element, key, or value class, and improve the error when a serializer factory in@Bind,@BindCollection, or@BindMapneeds the missing class - Fix the error message for duplicate tags in
TaggedFieldSerializer, which could throw anIndexOutOfBoundsExceptioninstead - Upgrade Objenesis from 3.4 to 3.6
Other Tasks:
- Test CI build against Java 23, 24, 25 and 26
- Switch release publishing to the Central Portal (#1188)
- Ensure main sources are always compiled for Java 8, regardless of the JDK used for the build
- Fix nondeterministic tests (#1219, #1220, #1227) (thanks @yonghanlin, @annhchen89)
- Documentation updates (#1245) (thanks @fzhyzamt)
- Upgrade Maven plugins and test dependencies
The full list of changes can be found here.
Many thanks to all contributors!
Upgrade Notes
For migration from previous major versions please check out the migration guide. We're asking the community to help and contribute this part: please edit the migration wiki page as you encounter any information or issues that might help others.
Behavioral changes
- Malformed input that declares array or string sizes larger than the remaining data now fails fast with a
KryoExceptioninstead of trying to allocate memory. Collection and map capacities are capped instead (#1284). FieldSerializerno longer wrapsOutOfMemoryErrorin aKryoException. Errors are now passed through unchanged.Kryo.reset()now also clears the generics stacks, so an exception during (de)serialization no longer leaves pooled or thread-local Kryo instances in a broken state (#1283).