Skip to content

Commit

Permalink
Merge branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanSweet committed Nov 21, 2013
2 parents 0a1c7e3 + aacf104 commit a0913de
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 114 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
![KryoNet](https://raw.github.com/wiki/EsotericSoftware/kryo/images/logo.jpg)

Kryo JARs are in the [download section](https://code.google.com/p/kryo/downloads/list) and [Maven Central](http://search.maven.org/#browse|1975274176). Latest snapshots of Kryo including the builds of a current trunk are in the [Sonatype Repository](https://oss.sonatype.org/content/repositories/snapshots/com/esotericsoftware/kryo/kryo).
[![Build Status](https://jenkins.inoio.de/buildStatus/icon?job=kryo)](https://jenkins.inoio.de/job/kryo/)

## New!
Please use the [Kryo discussion group](http://groups.google.com/group/kryo-users) for support.

Kryo 2.22 was just released. This release fixes a lot of reported issues and aims for improved stability and performance. It also introduces many new features, most notably that it can use Unsafe to read and write object memory directly, which is the absolute fastest way to do serialization, especially for large primitive arrays.
Kryo JARs are available on the [releases page](https://github.com/EsotericSoftware/kryo/releases) and at [Maven Central](http://search.maven.org/#browse|1975274176). Latest snapshots of Kryo including snapshot builds of master are in the [Sonatype Repository](https://oss.sonatype.org/content/repositories/snapshots/com/esotericsoftware/kryo/kryo).

For those using the maven repo: the main jar now contains the required dependencies (like e.g. asm, with "relocated" class names) to avoid conflicts with a different version used by your application. There's no longer a separate shaded jar.
## New in 2.22

The 2.22 release fixes many reported issues and improves stability and performance. It also introduces a number of new features, most notably that it can use Unsafe to read and write object memory directly. This is the absolute fastest way to do serialization, especially for large primitive arrays.

The Maven JARs now contain a "shaded" version of ObjectWeb's ASM library to avoid conflicts with a different ASM version in your application. There's no longer a separate shaded jar.

## Overview

Expand Down Expand Up @@ -527,3 +531,4 @@ There are a number of projects using Kryo. A few are listed below. Please post a
## Contact / Mailing list

You can use the [kryo mailing list](https://groups.google.com/forum/#!forum/kryo-users) for questions/discussions/support.

2 changes: 1 addition & 1 deletion src/com/esotericsoftware/kryo/Kryo.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver, S
addDefaultSerializer(boolean[].class, BooleanArraySerializer.class);
addDefaultSerializer(String[].class, StringArraySerializer.class);
addDefaultSerializer(Object[].class, ObjectArraySerializer.class);
addDefaultSerializer(KryoSerializable.class, KryoSerializableSerializer.class);
addDefaultSerializer(BigInteger.class, BigIntegerSerializer.class);
addDefaultSerializer(BigDecimal.class, BigDecimalSerializer.class);
addDefaultSerializer(Class.class, ClassSerializer.class);
Expand All @@ -186,7 +187,6 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver, S
addDefaultSerializer(Collection.class, CollectionSerializer.class);
addDefaultSerializer(TreeMap.class, TreeMapSerializer.class);
addDefaultSerializer(Map.class, MapSerializer.class);
addDefaultSerializer(KryoSerializable.class, KryoSerializableSerializer.class);
addDefaultSerializer(TimeZone.class, TimeZoneSerializer.class);
addDefaultSerializer(Calendar.class, CalendarSerializer.class);
lowPriorityDefaultSerializerCount = defaultSerializers.size();
Expand Down
36 changes: 25 additions & 11 deletions src/com/esotericsoftware/kryo/serializers/FieldSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class FieldSerializer<T> extends Serializer<T> implements Comparator<Fiel
private boolean useMemRegions = false;

/** If set, transient fields will be copied */
private final boolean copyTransient = true;
private boolean copyTransient = true;

/** If set, transient fields will be serialized */
private final boolean serializeTransient = false;
Expand All @@ -101,10 +101,9 @@ public class FieldSerializer<T> extends Serializer<T> implements Comparator<Fiel
Method unsafeMethod = unsafeUtilClass.getMethod("unsafe");
sortFieldsByOffsetMethod = unsafeUtilClass.getMethod("sortFieldsByOffset", List.class);
Object unsafe = unsafeMethod.invoke(null);
if(unsafe != null)
unsafeAvailable = true;
if (unsafe != null) unsafeAvailable = true;
} catch (Throwable e) {

if (TRACE) trace("kryo", "java.misc.Unsafe is not available");
}
}

Expand All @@ -121,8 +120,12 @@ public FieldSerializer (Kryo kryo, Class type) {
this.type = type;
this.typeParameters = type.getTypeParameters();
this.useAsmEnabled = kryo.getAsmEnabled();
if (!this.useAsmEnabled && !unsafeAvailable) {
this.useAsmEnabled = true;
if (TRACE) trace("kryo", "java.misc.Unsafe is unavailable. Using ASM instead.");
}
this.genericsUtil = new FieldSerializerGenericsUtil(this);
this.unsafeUtil = new FieldSerializerUnsafeUtil(this);
this.unsafeUtil = FieldSerializerUnsafeUtil.Factory.getInstance(this);
if (TRACE) trace("kryo", "FieldSerializer(Kryo, Class)");
rebuildCachedFields();
}
Expand All @@ -133,8 +136,12 @@ public FieldSerializer (Kryo kryo, Class type, Class[] generics) {
this.generics = generics;
this.typeParameters = type.getTypeParameters();
this.useAsmEnabled = kryo.getAsmEnabled();
if (!this.useAsmEnabled && !unsafeAvailable) {
this.useAsmEnabled = true;
if (TRACE) trace("kryo", "java.misc.Unsafe is unavailable. Using ASM instead.");
}
this.genericsUtil = new FieldSerializerGenericsUtil(this);
this.unsafeUtil = new FieldSerializerUnsafeUtil(this);
this.unsafeUtil = FieldSerializerUnsafeUtil.Factory.getInstance(this);
if (TRACE) trace("kryo", "FieldSerializer(Kryo, Class, Generics)");
rebuildCachedFields();
}
Expand Down Expand Up @@ -414,16 +421,19 @@ public void setFixedFieldTypes (boolean fixedFieldTypes) {
* @param setUseAsm If true, ASM will be used for fast serialization. If false, Unsafe will be used (default) */
public void setUseAsm (boolean setUseAsm) {
useAsmEnabled = setUseAsm;
if (!useAsmEnabled && !unsafeAvailable) {
useAsmEnabled = true;
if (TRACE) trace("kryo", "setUseAsm: java.misc.Unsafe is unavailable. Using ASM instead.");
}
// optimizeInts = useAsmBackend;
if (TRACE) trace("kryo", "setUseAsm: " + setUseAsm);
rebuildCachedFields();
}

// Uncomment this method, if we want to allow explicit control over copying of transient fields
// public void setCopyTransient (boolean setCopyTransient) {
// copyTransient = setCopyTransient;
// if(TRACE) trace("kryo", "setCopyTransient");
// }
// Enable/disable copying of transient fields
public void setCopyTransient (boolean setCopyTransient) {
copyTransient = setCopyTransient;
}

/** This method can be called for different fields having the same type. Even though the raw type is the same, if the type is
* generic, it could happen that different concrete classes are used to instantiate it. Therefore, in case of different
Expand Down Expand Up @@ -542,6 +552,10 @@ public boolean getUseAsmEnabled() {
public boolean getUseMemRegions() {
return useMemRegions;
}

public boolean getCopyTransient() {
return copyTransient;
}

/** Used by {@link #copy(Kryo, Object)} to create the new object. This can be overridden to customize object creation, eg to
* call a constructor with arguments. The default implementation uses {@link Kryo#newInstance(Class)}. */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,121 +1,45 @@
package com.esotericsoftware.kryo.serializers;

import static com.esotericsoftware.kryo.util.UnsafeUtil.unsafe;
import static com.esotericsoftware.minlog.Log.TRACE;
import static com.esotericsoftware.minlog.Log.trace;
package com.esotericsoftware.kryo.serializers;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.List;

import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField;
import com.esotericsoftware.kryo.serializers.UnsafeCacheFields.UnsafeRegionField;
import com.esotericsoftware.kryo.util.IntArray;
import com.esotericsoftware.reflectasm.FieldAccess;

/* Helper class for implementing FieldSerializer using Unsafe-based approach.
/* Helper interface for using Unsafe-based operations inside FieldSerializer.
* @author Roman Levenstein <[email protected]> */
final class FieldSerializerUnsafeUtil {
private FieldSerializer serializer;

public FieldSerializerUnsafeUtil (FieldSerializer serializer) {
this.serializer = serializer;
}
interface FieldSerializerUnsafeUtil {

/** Use Unsafe-based information about fields layout in memory to build a list of cached fields and memory regions representing
* consecutive fields in memory */
public void createUnsafeCacheFieldsAndRegions (List<Field> validFields, List<CachedField> cachedFields, int baseIndex,
IntArray useAsm) {
// Find adjacent fields of primitive types
long startPrimitives = 0;
long endPrimitives = 0;
boolean lastWasPrimitive = false;
int primitiveLength = 0;
int lastAccessIndex = -1;
Field lastField = null;
long fieldOffset = -1;
long fieldEndOffset = -1;
long lastFieldEndOffset = -1;
public abstract void createUnsafeCacheFieldsAndRegions (List<Field> validFields, List<CachedField> cachedFields,
int baseIndex, IntArray useAsm);

for (int i = 0, n = validFields.size(); i < n; i++) {
Field field = validFields.get(i);
public abstract long getObjectFieldOffset (Field field);

int accessIndex = -1;
if (serializer.access != null && useAsm.get(baseIndex + i) == 1)
accessIndex = ((FieldAccess)serializer.access).getIndex(field.getName());
static class Factory {
static Constructor<FieldSerializerUnsafeUtil> fieldSerializerUnsafeUtilConstructor;

fieldOffset = unsafe().objectFieldOffset(field);
fieldEndOffset = fieldOffset + fieldSizeOf(field.getType());
static {
try {
fieldSerializerUnsafeUtilConstructor = (Constructor<FieldSerializerUnsafeUtil>)FieldSerializer.class.getClassLoader()
.loadClass("com.esotericsoftware.kryo.serializers.FieldSerializerUnsafeUtilImpl")
.getConstructor(FieldSerializer.class);
} catch (Throwable e) {

if (!field.getType().isPrimitive() && lastWasPrimitive) {
// This is not a primitive field. Therefore, it marks
// the end of a region of primitive fields
endPrimitives = lastFieldEndOffset;
lastWasPrimitive = false;
if (primitiveLength > 1) {
if (TRACE)
trace("kryo", "Class " + serializer.getType().getName()
+ ". Found a set of consecutive primitive fields. Number of fields = " + primitiveLength
+ ". Byte length = " + (endPrimitives - startPrimitives) + " Start offset = " + startPrimitives
+ " endOffset=" + endPrimitives);
// TODO: register a region instead of a field
CachedField cf = new UnsafeRegionField(startPrimitives, (endPrimitives - startPrimitives));
cf.field = lastField;
cachedFields.add(cf);
} else {
if (lastField != null)
cachedFields.add(serializer.newCachedField(lastField, cachedFields.size(), lastAccessIndex));
}
cachedFields.add(serializer.newCachedField(field, cachedFields.size(), accessIndex));
} else if (!field.getType().isPrimitive()) {
cachedFields.add(serializer.newCachedField(field, cachedFields.size(), accessIndex));
} else if (!lastWasPrimitive) {
// If previous field was non primitive, it marks a start
// of a region of primitive fields
startPrimitives = fieldOffset;
lastWasPrimitive = true;
primitiveLength = 1;
} else {
primitiveLength++;
}

lastAccessIndex = accessIndex;
lastField = field;
lastFieldEndOffset = fieldEndOffset;
}

if (!serializer.getUseAsmEnabled() && serializer.getUseMemRegions() && lastWasPrimitive) {
endPrimitives = lastFieldEndOffset;
if (primitiveLength > 1) {
if (TRACE) {
trace("kryo", "Class " + serializer.getType().getName()
+ ". Found a set of consecutive primitive fields. Number of fields = " + primitiveLength + ". Byte length = "
+ (endPrimitives - startPrimitives) + " Start offset = " + startPrimitives + " endOffset=" + endPrimitives);
static FieldSerializerUnsafeUtil getInstance (FieldSerializer serializer) {
if (fieldSerializerUnsafeUtilConstructor != null) {
try {
return fieldSerializerUnsafeUtilConstructor.newInstance(serializer);
} catch (Exception e) {
}
// register a region instead of a field
CachedField cf = new UnsafeRegionField(startPrimitives, (endPrimitives - startPrimitives));
cf.field = lastField;
cachedFields.add(cf);
} else {
if (lastField != null) cachedFields.add(serializer.newCachedField(lastField, cachedFields.size(), lastAccessIndex));
}
return null;
}
}

/** Returns the in-memory size of a field which has a given class */
private int fieldSizeOf (Class<?> clazz) {
if (clazz == int.class || clazz == float.class) return 4;

if (clazz == long.class || clazz == double.class) return 8;

if (clazz == byte.class || clazz == boolean.class) return 1;

if (clazz == short.class || clazz == char.class) return 2;

// Everything else is a reference to an object, i.e. an address
return unsafe().addressSize();
}

long getObjectFieldOffset (Field field) {
return unsafe().objectFieldOffset(field);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.esotericsoftware.kryo.serializers;

import static com.esotericsoftware.kryo.util.UnsafeUtil.unsafe;
import static com.esotericsoftware.minlog.Log.TRACE;
import static com.esotericsoftware.minlog.Log.trace;

import java.lang.reflect.Field;
import java.util.List;

import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField;
import com.esotericsoftware.kryo.serializers.UnsafeCacheFields.UnsafeRegionField;
import com.esotericsoftware.kryo.util.IntArray;
import com.esotericsoftware.reflectasm.FieldAccess;

/* Helper class for implementing FieldSerializer using Unsafe-based approach.
* @author Roman Levenstein <[email protected]> */
final class FieldSerializerUnsafeUtilImpl implements FieldSerializerUnsafeUtil {
private FieldSerializer serializer;

public FieldSerializerUnsafeUtilImpl (FieldSerializer serializer) {
this.serializer = serializer;
}

public void createUnsafeCacheFieldsAndRegions (List<Field> validFields, List<CachedField> cachedFields, int baseIndex,
IntArray useAsm) {
// Find adjacent fields of primitive types
long startPrimitives = 0;
long endPrimitives = 0;
boolean lastWasPrimitive = false;
int primitiveLength = 0;
int lastAccessIndex = -1;
Field lastField = null;
long fieldOffset = -1;
long fieldEndOffset = -1;
long lastFieldEndOffset = -1;

for (int i = 0, n = validFields.size(); i < n; i++) {
Field field = validFields.get(i);

int accessIndex = -1;
if (serializer.access != null && useAsm.get(baseIndex + i) == 1)
accessIndex = ((FieldAccess)serializer.access).getIndex(field.getName());

fieldOffset = unsafe().objectFieldOffset(field);
fieldEndOffset = fieldOffset + fieldSizeOf(field.getType());

if (!field.getType().isPrimitive() && lastWasPrimitive) {
// This is not a primitive field. Therefore, it marks
// the end of a region of primitive fields
endPrimitives = lastFieldEndOffset;
lastWasPrimitive = false;
if (primitiveLength > 1) {
if (TRACE)
trace("kryo", "Class " + serializer.getType().getName()
+ ". Found a set of consecutive primitive fields. Number of fields = " + primitiveLength
+ ". Byte length = " + (endPrimitives - startPrimitives) + " Start offset = " + startPrimitives
+ " endOffset=" + endPrimitives);
// TODO: register a region instead of a field
CachedField cf = new UnsafeRegionField(startPrimitives, (endPrimitives - startPrimitives));
cf.field = lastField;
cachedFields.add(cf);
} else {
if (lastField != null)
cachedFields.add(serializer.newCachedField(lastField, cachedFields.size(), lastAccessIndex));
}
cachedFields.add(serializer.newCachedField(field, cachedFields.size(), accessIndex));
} else if (!field.getType().isPrimitive()) {
cachedFields.add(serializer.newCachedField(field, cachedFields.size(), accessIndex));
} else if (!lastWasPrimitive) {
// If previous field was non primitive, it marks a start
// of a region of primitive fields
startPrimitives = fieldOffset;
lastWasPrimitive = true;
primitiveLength = 1;
} else {
primitiveLength++;
}

lastAccessIndex = accessIndex;
lastField = field;
lastFieldEndOffset = fieldEndOffset;
}

if (!serializer.getUseAsmEnabled() && serializer.getUseMemRegions() && lastWasPrimitive) {
endPrimitives = lastFieldEndOffset;
if (primitiveLength > 1) {
if (TRACE) {
trace("kryo", "Class " + serializer.getType().getName()
+ ". Found a set of consecutive primitive fields. Number of fields = " + primitiveLength + ". Byte length = "
+ (endPrimitives - startPrimitives) + " Start offset = " + startPrimitives + " endOffset=" + endPrimitives);
}
// register a region instead of a field
CachedField cf = new UnsafeRegionField(startPrimitives, (endPrimitives - startPrimitives));
cf.field = lastField;
cachedFields.add(cf);
} else {
if (lastField != null) cachedFields.add(serializer.newCachedField(lastField, cachedFields.size(), lastAccessIndex));
}
}
}

/** Returns the in-memory size of a field which has a given class */
private int fieldSizeOf (Class<?> clazz) {
if (clazz == int.class || clazz == float.class) return 4;

if (clazz == long.class || clazz == double.class) return 8;

if (clazz == byte.class || clazz == boolean.class) return 1;

if (clazz == short.class || clazz == char.class) return 2;

// Everything else is a reference to an object, i.e. an address
return unsafe().addressSize();
}

public long getObjectFieldOffset (Field field) {
return unsafe().objectFieldOffset(field);
}
}
2 changes: 2 additions & 0 deletions src/com/esotericsoftware/kryo/util/UnsafeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public class UnsafeUtil {
longArrayBaseOffset = 0;
doubleArrayBaseOffset = 0;
_unsafe = null;
if (TRACE)
trace("kryo", "Running on Android platform. Use of java.misc.Unsafe should be disabled");
}
} catch (java.lang.Exception e) {
throw new RuntimeException(e);
Expand Down
Loading

0 comments on commit a0913de

Please sign in to comment.