ObjectIn/OutputStream improvements

Chris Hegarty chris.hegarty at oracle.com
Fri Jan 31 18:47:05 UTC 2014


Forwarding for correct core-libs-dev list.

-Chris.

On 31 Jan 2014, at 15:26, Robert Stupp <snazy at gmx.de> wrote:

> Hi Chris,
>  
> fine. I'm a bit proud that my 5ct help to improve JDK9 :)
>  
> The primitive buffer array size rounding was there to reduce the number of re-allocations when a class requires a slightly bigger primitive buffer than another. Maybe we can introduce some minimum buffer size - e.g. 64 or 128 bytes? This should be enough for most classes. Classes that require a bigger buffer will always force an extend of the buffer - rounded or not.
>  
> Robert
>  
>  
> Gesendet: Freitag, 31. Januar 2014 um 15:22 Uhr
> Von: "Chris Hegarty" <chris.hegarty at oracle.com>
> An: "Robert Stupp" <snazy at gmx.de>, core-libs-dev-request <core-libs-dev-request at openjdk.java.net>
> Betreff: Re: Aw: Re: ObjectIn/OutputStream improvements
> Hi Robert,
> 
> I think your patch can be split into two logical, independent, parts.
> The first is the use of unsafe to access the String UTF length. The
> seconds is to reuse, where possible, the existing StringBuilder
> instances, skipping of primitive/object reading/writing where
> applicable, and general cleanup.
> 
> Since this is a very sensitive area I would like to consider these
> separately. To that end, I have taken the changes that are applicable to
> the latter, removed any subjective stylistic changes, and made some
> additional cleanup improvements.
> 
> http://cr.openjdk.java.net/~chegar/serial_stupp.00/webrev/
> 
> Specifically,
> * I think for clarify and readability SerialCallbackContext
> checkAndSetUsed() should be invoked directly. It makes it very
> clear what the intent is.
> * Skip primitive/object reading/writing if no primitives/objects in
> the stream/class. ( I personally don't see any benefit of rounding
> up the size of the array, it just seems to add unnecessary
> complexity )
> * Move primitive types into getPrimitiveSignature for better reuse
> of code. This retains your change to not create the additional
> StringBuilder when it is not necessary.
> 
> I am currently running tests on this change.
> 
> -Chris.
> 
> On 07/01/14 13:03, Robert Stupp wrote:
> > Hi,
> > I've attached the diff to the original email - it has been stripped.
> > Here's a second try (inline).
> > I've already signed the OCA and it has been approved :)
> > Robert
> > # HG changeset patch
> > # User snazy
> > # Date 1387101091 -3600
> > # Sun Dec 15 10:51:31 2013 +0100
> > # Node ID 6d46d99212453017c88417678d08dc8f10da9606
> > # Parent 9e1be800420265e5dcf264a7ed4abb6f230dd19d
> > Removed some unnecessary variable assignments.
> > ObjectInputStream:
> > - skip primitive/object reading if no primitive/objects in class
> > - use shared StringBuilder for string reading (prevent superfluous
> > object allocations)
> > ObjectOutputStream:
> > - skip primitive/object writing if no primitive/objects in class
> > - use unsafe access to calculate UTF-length
> > - use unsafe access in readBytes() and writeChars() methods to access
> > String value field
> > - removed cbuf field
> > ObjectStreamClass/ObjectStreamField:
> > - minor improvement in getClassSignature ; share method code with
> > ObjectStreamField
> > diff --git a/src/share/classes/java/io/ObjectInputStream.java
> > b/src/share/classes/java/io/ObjectInputStream.java
> > --- a/src/share/classes/java/io/ObjectInputStream.java
> > +++ b/src/share/classes/java/io/ObjectInputStream.java
> > @@ -39,8 +39,8 @@
> > import java.util.HashMap;
> > import java.util.concurrent.ConcurrentHashMap;
> > import java.util.concurrent.ConcurrentMap;
> > -import java.util.concurrent.atomic.AtomicBoolean;
> > import static java.io.ObjectStreamClass.processQueue;
> > +
> > import sun.reflect.misc.ReflectUtil;
> >
> > /**
> > @@ -534,7 +534,7 @@
> > if (ctx == null) {
> > throw new NotActiveException("not in call to readObject");
> > }
> > - Object curObj = ctx.getObj();
> > + ctx.getObj();
> > ObjectStreamClass curDesc = ctx.getDesc();
> > bin.setBlockDataMode(false);
> > GetFieldImpl getField = new GetFieldImpl(curDesc);
> > @@ -1597,7 +1597,7 @@
> > int descHandle = handles.assign(unshared ? unsharedMarker : desc);
> > passHandle = NULL_HANDLE;
> >
> > - ObjectStreamClass readDesc = null;
> > + ObjectStreamClass readDesc;
> > try {
> > readDesc = readClassDescriptor();
> > } catch (ClassNotFoundException ex) {
> > @@ -1976,29 +1976,34 @@
> > }
> >
> > int primDataSize = desc.getPrimDataSize();
> > - if (primVals == null || primVals.length < primDataSize) {
> > - primVals = new byte[primDataSize];
> > - }
> > - bin.readFully(primVals, 0, primDataSize, false);
> > - if (obj != null) {
> > - desc.setPrimFieldValues(obj, primVals);
> > - }
> > -
> > - int objHandle = passHandle;
> > - ObjectStreamField[] fields = desc.getFields(false);
> > - Object[] objVals = new Object[desc.getNumObjFields()];
> > - int numPrimFields = fields.length - objVals.length;
> > - for (int i = 0; i < objVals.length; i++) {
> > - ObjectStreamField f = fields[numPrimFields + i];
> > - objVals[i] = readObject0(f.isUnshared());
> > - if (f.getField() != null) {
> > - handles.markDependency(objHandle, passHandle);
> > + if (primDataSize > 0) {
> > + if (primVals == null || primVals.length < primDataSize) {
> > + primVals = new byte[ ((primDataSize>>4)+1)<<4 ];
> > + }
> > + bin.readFully(primVals, 0, primDataSize, false);
> > + if (obj != null) {
> > + desc.setPrimFieldValues(obj, primVals);
> > }
> > }
> > - if (obj != null) {
> > - desc.setObjFieldValues(obj, objVals);
> > +
> > + int numObjFields = desc.getNumObjFields();
> > + if (numObjFields > 0) {
> > + int objHandle = passHandle;
> > + ObjectStreamField[] fields = desc.getFields(false);
> > + Object[] objVals = new Object[numObjFields];
> > + int numPrimFields = fields.length - objVals.length;
> > + for (int i = 0; i < objVals.length; i++) {
> > + ObjectStreamField f = fields[numPrimFields + i];
> > + objVals[i] = readObject0(f.isUnshared());
> > + if (f.getField() != null) {
> > + handles.markDependency(objHandle, passHandle);
> > + }
> > + }
> > + if (obj != null) {
> > + desc.setObjFieldValues(obj, objVals);
> > + }
> > + passHandle = objHandle;
> > }
> > - passHandle = objHandle;
> > }
> >
> > /**
> > @@ -2377,8 +2382,10 @@
> > private final byte[] buf = new byte[MAX_BLOCK_SIZE];
> > /** buffer for reading block data headers */
> > private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
> > - /** char buffer for fast string reads */
> > + /** char buffer for fast string reads - used by {@link
> > #readUTFSpan(long)} */
> > private final char[] cbuf = new char[CHAR_BUF_SIZE];
> > + /** shared string builder for less object allocations - used by
> > {@link #readUTFBody(long)}, {@link #readUTFChar(long)} and {@link
> > #readUTFSpan(long)} */
> > + private final StringBuilder sbuf = new
> > StringBuilder(CHAR_BUF_SIZE);
> >
> > /** block data mode */
> > private boolean blkmode = false;
> > @@ -3044,19 +3051,19 @@
> > * utflen bytes.
> > */
> > private String readUTFBody(long utflen) throws IOException {
> > - StringBuilder sbuf = new StringBuilder();
> > if (!blkmode) {
> > end = pos = 0;
> > }
> >
> > + sbuf.setLength(0);
> > while (utflen > 0) {
> > int avail = end - pos;
> > if (avail >= 3 || (long) avail == utflen) {
> > - utflen -= readUTFSpan(sbuf, utflen);
> > + utflen -= readUTFSpan(utflen);
> > } else {
> > if (blkmode) {
> > // near block boundary, read one byte at a time
> > - utflen -= readUTFChar(sbuf, utflen);
> > + utflen -= readUTFChar(utflen);
> > } else {
> > // shift and refill buffer manually
> > if (avail > 0) {
> > @@ -3076,10 +3083,10 @@
> > * Reads span of UTF-encoded characters out of internal buffer
> > * (starting at offset pos and ending at or before offset end),
> > * consuming no more than utflen bytes. Appends read
> > characters to
> > - * sbuf. Returns the number of bytes consumed.
> > + * {@link #sbuf}. Returns the number of bytes consumed.
> > */
> > - private long readUTFSpan(StringBuilder sbuf, long utflen)
> > - throws IOException
> > + private long readUTFSpan(long utflen)
> > + throws IOException
> > {
> > int cpos = 0;
> > int start = pos;
> > @@ -3111,19 +3118,19 @@
> > throw new UTFDataFormatException();
> > }
> > cbuf[cpos++] = (char) (((b1 & 0x1F) << 6) |
> > - ((b2 & 0x3F) << 0));
> > + (b2 & 0x3F));
> > break;
> >
> > case 14: // 3 byte format: 1110xxxx 10xxxxxx
> > 10xxxxxx
> > b3 = buf[pos + 1];
> > - b2 = buf[pos + 0];
> > + b2 = buf[pos ];
> > pos += 2;
> > if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) !=
> > 0x80) {
> > throw new UTFDataFormatException();
> > }
> > cbuf[cpos++] = (char) (((b1 & 0x0F) << 12) |
> > - ((b2 & 0x3F) << 6) |
> > - ((b3 & 0x3F) << 0));
> > + ((b2 & 0x3F) << 6) |
> > + (b3 & 0x3F));
> > break;
> >
> > default: // 10xx xxxx, 1111 xxxx
> > @@ -3150,12 +3157,12 @@
> >
> > /**
> > * Reads in single UTF-encoded character one byte at a time,
> > appends
> > - * the character to sbuf, and returns the number of bytes consumed.
> > + * the character to {@link #sbuf}, and returns the number of
> > bytes consumed.
> > * This method is used when reading in UTF strings written in
> > block
> > * data mode to handle UTF-encoded characters which (potentially)
> > * straddle block-data boundaries.
> > */
> > - private int readUTFChar(StringBuilder sbuf, long utflen)
> > + private int readUTFChar(long utflen)
> > throws IOException
> > {
> > int b1, b2, b3;
> > @@ -3181,8 +3188,7 @@
> > if ((b2 & 0xC0) != 0x80) {
> > throw new UTFDataFormatException();
> > }
> > - sbuf.append((char) (((b1 & 0x1F) << 6) |
> > - ((b2 & 0x3F) << 0)));
> > + sbuf.append((char) (((b1 & 0x1F) << 6) | (b2 & 0x3F)));
> > return 2;
> >
> > case 14: // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx
> > @@ -3198,8 +3204,8 @@
> > throw new UTFDataFormatException();
> > }
> > sbuf.append((char) (((b1 & 0x0F) << 12) |
> > - ((b2 & 0x3F) << 6) |
> > - ((b3 & 0x3F) << 0)));
> > + ((b2 & 0x3F) << 6) |
> > + (b3 & 0x3F)));
> > return 3;
> >
> > default: // 10xx xxxx, 1111 xxxx
> > diff --git a/src/share/classes/java/io/ObjectOutputStream.java
> > b/src/share/classes/java/io/ObjectOutputStream.java
> > --- a/src/share/classes/java/io/ObjectOutputStream.java
> > +++ b/src/share/classes/java/io/ObjectOutputStream.java
> > @@ -35,7 +35,8 @@
> > import java.util.concurrent.ConcurrentHashMap;
> > import java.util.concurrent.ConcurrentMap;
> > import static java.io.ObjectStreamClass.processQueue;
> > -import java.io.SerialCallbackContext;
> > +
> > +import sun.misc.Unsafe;
> > import sun.reflect.misc.ReflectUtil;
> >
> > /**
> > @@ -458,7 +459,7 @@
> > if (ctx == null) {
> > throw new NotActiveException("not in call to
> > writeObject");
> > }
> > - Object curObj = ctx.getObj();
> > + ctx.getObj();
> > ObjectStreamClass curDesc = ctx.getDesc();
> > curPut = new PutFieldImpl(curDesc);
> > }
> > @@ -1300,7 +1301,7 @@
> > */
> > private void writeString(String str, boolean unshared) throws
> > IOException {
> > handles.assign(unshared ? null : str);
> > - long utflen = bout.getUTFLength(str);
> > + long utflen = BlockDataOutputStream.getUTFLength(str);
> > if (utflen <= 0xFFFF) {
> > bout.writeByte(TC_STRING);
> > bout.writeUTF(str, utflen);
> > @@ -1527,29 +1528,34 @@
> > desc.checkDefaultSerialize();
> >
> > int primDataSize = desc.getPrimDataSize();
> > - if (primVals == null || primVals.length < primDataSize) {
> > - primVals = new byte[primDataSize];
> > + if (primDataSize > 0) {
> > + if (primVals == null || primVals.length < primDataSize) {
> > + primVals = new byte[ ((primDataSize>>4)+1)<<4 ];
> > + }
> > + desc.getPrimFieldValues(obj, primVals);
> > + bout.write(primVals, 0, primDataSize, false);
> > }
> > - desc.getPrimFieldValues(obj, primVals);
> > - bout.write(primVals, 0, primDataSize, false);
> >
> > - ObjectStreamField[] fields = desc.getFields(false);
> > - Object[] objVals = new Object[desc.getNumObjFields()];
> > - int numPrimFields = fields.length - objVals.length;
> > - desc.getObjFieldValues(obj, objVals);
> > - for (int i = 0; i < objVals.length; i++) {
> > - if (extendedDebugInfo) {
> > - debugInfoStack.push(
> > - "field (class \"" + desc.getName() + "\", name: \"" +
> > - fields[numPrimFields + i].getName() + "\", type: \"" +
> > - fields[numPrimFields + i].getType() + "\")");
> > - }
> > - try {
> > - writeObject0(objVals[i],
> > - fields[numPrimFields + i].isUnshared());
> > - } finally {
> > + int numObjFields = desc.getNumObjFields();
> > + if (numObjFields > 0) {
> > + ObjectStreamField[] fields = desc.getFields(false);
> > + Object[] objVals = new Object[numObjFields];
> > + int numPrimFields = fields.length - objVals.length;
> > + desc.getObjFieldValues(obj, objVals);
> > + for (int i = 0; i < objVals.length; i++) {
> > if (extendedDebugInfo) {
> > - debugInfoStack.pop();
> > + debugInfoStack.push(
> > + "field (class \"" + desc.getName() + "\", name:
> > \"" +
> > + fields[numPrimFields + i].getName() + "\",
> > type: \"" +
> > + fields[numPrimFields + i].getType() + "\")");
> > + }
> > + try {
> > + writeObject0(objVals[i],
> > + fields[numPrimFields + i].isUnshared());
> > + } finally {
> > + if (extendedDebugInfo) {
> > + debugInfoStack.pop();
> > + }
> > }
> > }
> > }
> > @@ -1743,15 +1749,11 @@
> > private static final int MAX_BLOCK_SIZE = 1024;
> > /** maximum data block header length */
> > private static final int MAX_HEADER_SIZE = 5;
> > - /** (tunable) length of char buffer (for writing strings) */
> > - private static final int CHAR_BUF_SIZE = 256;
> >
> > /** buffer for writing general/block data */
> > private final byte[] buf = new byte[MAX_BLOCK_SIZE];
> > /** buffer for writing block data headers */
> > private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
> > - /** char buffer for fast string writes */
> > - private final char[] cbuf = new char[CHAR_BUF_SIZE];
> >
> > /** block data mode */
> > private boolean blkmode = false;
> > @@ -1763,6 +1765,18 @@
> > /** loopback stream (for data writes that span data blocks) */
> > private final DataOutputStream dout;
> >
> > + /** use unsafe to directly access value field in
> > java.lang.String */
> > + private static final Unsafe unsafe = Unsafe.getUnsafe();
> > + /** use field offset to directly access value field in
> > java.lang.String */
> > + private static final long stringValueOffset;
> > + static {
> > + try {
> > + stringValueOffset =
> > unsafe.objectFieldOffset(String.class.getDeclaredField("value"));
> > + } catch (NoSuchFieldException e) {
> > + throw new InternalError(e);
> > + }
> > + }
> > +
> > /**
> > * Creates new BlockDataOutputStream on top of given
> > underlying stream.
> > * Block data mode is turned off by default.
> > @@ -1972,35 +1986,23 @@
> > }
> >
> > public void writeBytes(String s) throws IOException {
> > - int endoff = s.length();
> > - int cpos = 0;
> > - int csize = 0;
> > + char[] sChars = (char[])unsafe.getObject(s, stringValueOffset);
> > + int endoff = sChars.length;
> > for (int off = 0; off < endoff; ) {
> > - if (cpos >= csize) {
> > - cpos = 0;
> > - csize = Math.min(endoff - off, CHAR_BUF_SIZE);
> > - s.getChars(off, off + csize, cbuf, 0);
> > - }
> > if (pos >= MAX_BLOCK_SIZE) {
> > drain();
> > }
> > - int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
> > + int n = Math.min(endoff - off, MAX_BLOCK_SIZE - pos);
> > int stop = pos + n;
> > while (pos < stop) {
> > - buf[pos++] = (byte) cbuf[cpos++];
> > + buf[pos++] = (byte) sChars[off++];
> > }
> > - off += n;
> > }
> > }
> >
> > public void writeChars(String s) throws IOException {
> > - int endoff = s.length();
> > - for (int off = 0; off < endoff; ) {
> > - int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
> > - s.getChars(off, off + csize, cbuf, 0);
> > - writeChars(cbuf, 0, csize);
> > - off += csize;
> > - }
> > + char[] sChars = (char[])unsafe.getObject(s, stringValueOffset);
> > + writeChars(sChars, 0, sChars.length);
> > }
> >
> > public void writeUTF(String s) throws IOException {
> > @@ -2130,25 +2132,21 @@
> > }
> >
> > /**
> > - * Returns the length in bytes of the UTF encoding of the given
> > string.
> > + * Returns the length in bytes of the UTF encoding of this
> > given string.
> > */
> > - long getUTFLength(String s) {
> > - int len = s.length();
> > + static long getUTFLength(String s) {
> > + char[] value = (char[])unsafe.getObject(s, stringValueOffset);
> > + int len = value.length;
> > long utflen = 0;
> > for (int off = 0; off < len; ) {
> > - int csize = Math.min(len - off, CHAR_BUF_SIZE);
> > - s.getChars(off, off + csize, cbuf, 0);
> > - for (int cpos = 0; cpos < csize; cpos++) {
> > - char c = cbuf[cpos];
> > - if (c >= 0x0001 && c <= 0x007F) {
> > - utflen++;
> > - } else if (c > 0x07FF) {
> > - utflen += 3;
> > - } else {
> > - utflen += 2;
> > - }
> > + char c = value[off++];
> > + if (c >= 0x0001 && c <= 0x007F) {
> > + utflen++;
> > + } else if (c > 0x07FF) {
> > + utflen += 3;
> > + } else {
> > + utflen += 2;
> > }
> > - off += csize;
> > }
> > return utflen;
> > }
> > @@ -2198,40 +2196,36 @@
> > * 8-byte length header) of the UTF encoding for the given string.
> > */
> > private void writeUTFBody(String s) throws IOException {
> > + char[] sChars = (char[])unsafe.getObject(s, stringValueOffset);
> > + int len = sChars.length;
> > int limit = MAX_BLOCK_SIZE - 3;
> > - int len = s.length();
> > for (int off = 0; off < len; ) {
> > - int csize = Math.min(len - off, CHAR_BUF_SIZE);
> > - s.getChars(off, off + csize, cbuf, 0);
> > - for (int cpos = 0; cpos < csize; cpos++) {
> > - char c = cbuf[cpos];
> > - if (pos <= limit) {
> > - if (c <= 0x007F && c != 0) {
> > - buf[pos++] = (byte) c;
> > - } else if (c > 0x07FF) {
> > - buf[pos + 2] = (byte) (0x80 | ((c >> 0) &
> > 0x3F));
> > - buf[pos + 1] = (byte) (0x80 | ((c >> 6) &
> > 0x3F));
> > - buf[pos + 0] = (byte) (0xE0 | ((c >> 12) &
> > 0x0F));
> > - pos += 3;
> > - } else {
> > - buf[pos + 1] = (byte) (0x80 | ((c >> 0) &
> > 0x3F));
> > - buf[pos + 0] = (byte) (0xC0 | ((c >> 6) &
> > 0x1F));
> > - pos += 2;
> > - }
> > - } else { // write one byte at a time to
> > normalize block
> > - if (c <= 0x007F && c != 0) {
> > - write(c);
> > - } else if (c > 0x07FF) {
> > - write(0xE0 | ((c >> 12) & 0x0F));
> > - write(0x80 | ((c >> 6) & 0x3F));
> > - write(0x80 | ((c >> 0) & 0x3F));
> > - } else {
> > - write(0xC0 | ((c >> 6) & 0x1F));
> > - write(0x80 | ((c >> 0) & 0x3F));
> > - }
> > + char c = sChars[off++];
> > + if (pos <= limit) {
> > + if (c <= 0x007F && c != 0) {
> > + buf[pos++] = (byte) c;
> > + } else if (c > 0x07FF) {
> > + buf[pos + 2] = (byte) (0x80 | ( c & 0x3F));
> > + buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
> > + buf[pos ] = (byte) (0xE0 | ((c >> 12) & 0x0F));
> > + pos += 3;
> > + } else {
> > + buf[pos + 1] = (byte) (0x80 | ( c & 0x3F));
> > + buf[pos ] = (byte) (0xC0 | ((c >> 6) & 0x1F));
> > + pos += 2;
> > + }
> > + } else { // write one byte at a time to normalize block
> > + if (c <= 0x007F && c != 0) {
> > + write(c);
> > + } else if (c > 0x07FF) {
> > + write(0xE0 | ((c >> 12) & 0x0F));
> > + write(0x80 | ((c >> 6) & 0x3F));
> > + write(0x80 | ( c & 0x3F));
> > + } else {
> > + write(0xC0 | ((c >> 6) & 0x1F));
> > + write(0x80 | ( c & 0x3F));
> > }
> > }
> > - off += csize;
> > }
> > }
> > }
> > @@ -2464,7 +2458,10 @@
> > StringBuilder buffer = new StringBuilder();
> > if (!stack.isEmpty()) {
> > for(int i = stack.size(); i > 0; i-- ) {
> > - buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : ""));
> > + buffer.append(stack.get(i - 1));
> > + if (i!=1) {
> > + buffer.append('\n');
> > + }
> > }
> > }
> > return buffer.toString();
> > diff --git a/src/share/classes/java/io/ObjectStreamClass.java
> > b/src/share/classes/java/io/ObjectStreamClass.java
> > --- a/src/share/classes/java/io/ObjectStreamClass.java
> > +++ b/src/share/classes/java/io/ObjectStreamClass.java
> > @@ -1474,7 +1474,28 @@
> > /**
> > * Returns JVM type signature for given class.
> > */
> > - private static String getClassSignature(Class<?> cl) {
> > + static String getClassSignature(Class<?> cl) {
> > + if (cl.isPrimitive())
> > + if (cl == Integer.TYPE) {
> > + return "I";
> > + } else if (cl == Byte.TYPE) {
> > + return "B";
> > + } else if (cl == Long.TYPE) {
> > + return "J";
> > + } else if (cl == Float.TYPE) {
> > + return "F";
> > + } else if (cl == Double.TYPE) {
> > + return "D";
> > + } else if (cl == Short.TYPE) {
> > + return "S";
> > + } else if (cl == Character.TYPE) {
> > + return "C";
> > + } else if (cl == Boolean.TYPE) {
> > + return "Z";
> > + } else if (cl == Void.TYPE) {
> > + return "V";
> > + }
> > +
> > StringBuilder sbuf = new StringBuilder();
> > while (cl.isArray()) {
> > sbuf.append('[');
> > @@ -1503,7 +1524,7 @@
> > throw new InternalError();
> > }
> > } else {
> > - sbuf.append('L' + cl.getName().replace('.', '/') + ';');
> > + sbuf.append('L').append(cl.getName().replace('.',
> > '/')).append(';');
> > }
> > return sbuf.toString();
> > }
> > diff --git a/src/share/classes/java/io/ObjectStreamField.java
> > b/src/share/classes/java/io/ObjectStreamField.java
> > --- a/src/share/classes/java/io/ObjectStreamField.java
> > +++ b/src/share/classes/java/io/ObjectStreamField.java
> > @@ -91,7 +91,7 @@
> > this.name = name;
> > this.type = type;
> > this.unshared = unshared;
> > - signature = getClassSignature(type).intern();
> > + signature = ObjectStreamClass.getClassSignature(type).intern();
> > field = null;
> > }
> >
> > @@ -137,7 +137,7 @@
> > name = field.getName();
> > Class<?> ftype = field.getType();
> > type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
> > - signature = getClassSignature(ftype).intern();
> > + signature = ObjectStreamClass.getClassSignature(ftype).intern();
> > }
> >
> > /**
> > @@ -286,41 +286,4 @@
> > String getSignature() {
> > return signature;
> > }
> > -
> > - /**
> > - * Returns JVM type signature for given class.
> > - */
> > - private static String getClassSignature(Class<?> cl) {
> > - StringBuilder sbuf = new StringBuilder();
> > - while (cl.isArray()) {
> > - sbuf.append('[');
> > - cl = cl.getComponentType();
> > - }
> > - if (cl.isPrimitive()) {
> > - if (cl == Integer.TYPE) {
> > - sbuf.append('I');
> > - } else if (cl == Byte.TYPE) {
> > - sbuf.append('B');
> > - } else if (cl == Long.TYPE) {
> > - sbuf.append('J');
> > - } else if (cl == Float.TYPE) {
> > - sbuf.append('F');
> > - } else if (cl == Double.TYPE) {
> > - sbuf.append('D');
> > - } else if (cl == Short.TYPE) {
> > - sbuf.append('S');
> > - } else if (cl == Character.TYPE) {
> > - sbuf.append('C');
> > - } else if (cl == Boolean.TYPE) {
> > - sbuf.append('Z');
> > - } else if (cl == Void.TYPE) {
> > - sbuf.append('V');
> > - } else {
> > - throw new InternalError();
> > - }
> > - } else {
> > - sbuf.append('L' + cl.getName().replace('.', '/') + ';');
> > - }
> > - return sbuf.toString();
> > - }
> > }
> > *Gesendet:* Dienstag, 07. Januar 2014 um 10:05 Uhr
> > *Von:* "Chris Hegarty" <chris.hegarty at oracle.com>
> > *An:* "Robert Stupp" <snazy at gmx.de>
> > *Cc:* "core-libs-dev at openjdk.java.net Libs" <core-libs-dev at openjdk.java.net>
> > *Betreff:* Re: ObjectIn/OutputStream improvements
> > On 15 Dec 2013, at 10:29, Robert Stupp <snazy at gmx.de> wrote:
> >
> > > Hi,
> > >
> > > I digged through the object serialization code and found some lines
> > that could be optimized to reduce the number of calls to
> > System.arraycopy() and temporary object allocations especially during
> > string (de)serialization.
> > > In short sentences the changes are:
> > > ObjectInputStream:
> > > - skip primitive/object reading if no primitive/objects in class
> > (defaultReadFields method)
> > > - use shared StringBuilder for string reading (prevent superfluous
> > object allocations of one StingBuilder and one implicit char[] for each
> > string being deserialized)
> > > ObjectOutputStream:
> > > - skip primitive/object writing if no primitive/objects in class
> > (defaultWriteFields method)
> > > - use unsafe access to calculate UTF-length
> > > - use unsafe access in readBytes() and writeChars() methods to access
> > String value field
> > > - removed cbuf field
> > > ObjectStreamClass/ObjectStreamField:
> > > - minor improvement in getClassSignature ; share method code with
> > ObjectStreamField (return constant string for primitives)
> > >
> > > I have tested the changes in a big Java installation at a customer
> > (backported the Java8 ObjectIn/OutputStream including the changes to
> > Java6) and a long running real application performance test resulted in
> > reduced CPU usage (from about 60% per server to 50%).
> > > The changes I made in openjdk8 pass all tests.
> > >
> > > Since I have no experience how to contribute code to openjdk in form
> > of a push/changeset I have added the diff (hg export -g) to this email.
> >
> > Did you attach the diffs? I don’t see any attachment, it may be that the
> > attachment was stripped. Can you try resending inline?
> >
> > You should take a look at the OpenJDK How to Contribute page [1]. Paying
> > particular attention to the OCA, without it we will not be able to move
> > your patch forward.
> >
> > Thanks,
> > -Chris.
> >
> > [1] http://openjdk.java.net/contribute/
> >
> > >
> > > Robert
> > >
> > >




More information about the core-libs-dev mailing list