public class Serializer
extends Object
The Serializer can perform better than ObjectOutputStream
and DataOutputStream
, with respect to encoding primary types, because it
uses a more compact format (containing no BlockHeader) and simpler call stack
involving BigEndianCodec
, as compared to using an OutputStream
wrapper on top of Bits
.
For Strings, the UTF encoding for ObjectOutputStream and DataOutputStream has a 2^16=64K length limitation, which is often too restrictive. Serializer has a 2^32=4G String length limitation, which is generally more than enough. For pure ASCII character Strings, the encoding performance is almost the same, if not better, than ObjectOutputStream and DataOutputStream. For Strings containing non-ASCII characters, the Serializer encodes each char to two bytes rather than performing UTF encoding. There is a trade-off between CPU/memory performance and compression rate.
UTF encoding uses more CPU cycles to detect the unicode range for each char
and the resulting output is variable length, which increases the memory
burden when preparing the decoding buffer. Whereas, encoding each char to two
bytes allows for better CPU/memory performance. Although inefficient with
compression rates in comparison to UTF encoding, the char to two byte
approach significantly simplifies the encoder's logic and the output length
is predictably based on the length of the String, so the decoder can manage
its decoding buffer efficiently. On average, a system uses more ASCII String
scheming than non-ASCII String scheming. In most cases, when all system
internal Strings are ASCII Strings and only Strings holding user input
information can have non-ASCII characters, this Serializer performs best. In
other cases, developers should consider using ObjectOutputStream
or
DataOutputStream
.
For ordinary Objects, all primary type wrappers are encoded to their raw
values with one byte type headers. This is much more efficient than
ObjectOutputStream's serialization format for primary type wrappers. Strings
are output in the same way as writeString(String)
, but also with one
byte type headers. Objects are serialized by a new ObjectOutputStream, so no
reference handler can be used across Object serialization. This is done
intentionally to isolate each object. The Serializer is highly optimized for
serializing primary types, but is not as good as ObjectOutputStream for
serializing complex objects.
On object serialization, the Serializer uses the ClassLoaderPool
to
look up the servlet context name corresponding to the object's ClassLoader.
The servlet context name is written to the serialization stream. On object
deserialization, the Deserializer
uses the ClassLoaderPool to look up
the ClassLoader corresponding to the servlet context name read from the
deserialization stream. ObjectOutputStream and ObjectInputStream lack these
features, making Serializer and Deserializer better choices for
ClassLoader-aware Object serialization/deserialization, especially when
plugins are involved.
Deserializer
Modifier and Type | Class and Description |
---|---|
protected static class |
Serializer.BufferNode |
protected class |
Serializer.BufferOutputStream |
protected static class |
Serializer.BufferQueue
Represents a descending
byte[] queue ordered by array
length. |
Modifier and Type | Field and Description |
---|---|
protected byte[] |
buffer |
protected static ThreadLocal<Reference<Serializer.BufferQueue>> |
bufferQueueThreadLocal
Softens the local thread's pooled buffer memory.
|
protected int |
index |
protected static int |
THREADLOCAL_BUFFER_COUNT_LIMIT |
protected static int |
THREADLOCAL_BUFFER_COUNT_MIN |
protected static int |
THREADLOCAL_BUFFER_SIZE_LIMIT |
protected static int |
THREADLOCAL_BUFFER_SIZE_MIN |
Constructor and Description |
---|
Serializer() |
Modifier and Type | Method and Description |
---|---|
protected byte[] |
getBuffer(int ensureExtraSpace)
Returns the required buffer length.
|
ByteBuffer |
toByteBuffer() |
void |
writeBoolean(boolean b) |
void |
writeByte(byte b) |
void |
writeChar(char c) |
void |
writeDouble(double d) |
void |
writeFloat(float f) |
void |
writeInt(int i) |
void |
writeLong(long l) |
void |
writeObject(Serializable serializable) |
void |
writeShort(short s) |
void |
writeString(String s) |
void |
writeTo(OutputStream outputStream) |
protected static final int THREADLOCAL_BUFFER_COUNT_LIMIT
protected static final int THREADLOCAL_BUFFER_COUNT_MIN
protected static final int THREADLOCAL_BUFFER_SIZE_LIMIT
protected static final int THREADLOCAL_BUFFER_SIZE_MIN
protected static final ThreadLocal<Reference<Serializer.BufferQueue>> bufferQueueThreadLocal
Technically, we should soften each pooled buffer individually to achieve
the best garbage collection (GC) interaction. However, that increases
complexity of pooled buffer access and also burdens the GC's SoftReference
process, hurting performance.
Here, the entire ThreadLocal BufferQueue is softened. For threads that do serializing often, its BufferQueue will most likely stay valid. For threads that do serializing only occasionally, its BufferQueue will most likely be released by GC.
protected byte[] buffer
protected int index
public ByteBuffer toByteBuffer()
public void writeBoolean(boolean b)
public void writeByte(byte b)
public void writeChar(char c)
public void writeDouble(double d)
public void writeFloat(float f)
public void writeInt(int i)
public void writeLong(long l)
public void writeObject(Serializable serializable)
public void writeShort(short s)
public void writeString(String s)
public void writeTo(OutputStream outputStream) throws IOException
IOException
protected final byte[] getBuffer(int ensureExtraSpace)
ensureExtraSpace
- the extra byte space required to meet the
buffer's minimum length