You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2015/06/30 12:52:07 UTC

[46/50] [abbrv] incubator-ignite git commit: ignite-950: protocol optimizations

ignite-950: protocol optimizations


Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/2e8bdec6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/2e8bdec6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/2e8bdec6

Branch: refs/heads/ignite-950
Commit: 2e8bdec625337ca33b3c58fbd70fae6f40104d2f
Parents: f746e4b
Author: Denis Magda <dm...@gridgain.com>
Authored: Mon Jun 29 11:00:03 2015 +0300
Committer: Denis Magda <dm...@gridgain.com>
Committed: Mon Jun 29 11:00:03 2015 +0300

----------------------------------------------------------------------
 .../cache/CacheIndexedObjectImpl.java           |  13 +-
 .../IgniteCacheObjectProcessorImpl.java         |   4 +-
 .../ignite/internal/util/io/GridDataInput.java  |   9 ++
 .../internal/util/io/GridUnsafeDataInput.java   |   5 +
 .../optimized/OptimizedClassDescriptor.java     |  15 +-
 .../OptimizedMarshalAwareMetaCollector.java     |   2 +-
 .../optimized/OptimizedMarshallerExt.java       |  10 +-
 .../optimized/OptimizedObjectInputStream.java   | 101 ++++++------
 .../optimized/OptimizedObjectMetadata.java      | 159 +++++++------------
 .../optimized/OptimizedObjectOutputStream.java  |  73 ++++-----
 .../OptimizedObjectOutputStreamExt.java         |  86 ++--------
 .../OptimizedMarshallerExtSelfTest.java         |  29 +++-
 12 files changed, 211 insertions(+), 295 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheIndexedObjectImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheIndexedObjectImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheIndexedObjectImpl.java
index c51d98c..8cf7085 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheIndexedObjectImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheIndexedObjectImpl.java
@@ -27,6 +27,7 @@ import sun.misc.*;
 
 import java.io.*;
 import java.nio.*;
+import java.util.concurrent.*;
 
 /**
  * Cache object implementation for classes that support footer injection is their serialized form thus enabling fields
@@ -92,6 +93,9 @@ public class CacheIndexedObjectImpl extends CacheObjectAdapter {
     public CacheIndexedObjectImpl(Object val, byte[] valBytes, int start, int len) {
         assert val != null || (valBytes != null && start >= 0 && len > 0);
 
+        if (valBytes != null && val != null)
+            val = null;
+
         this.val = val;
         this.valBytes = valBytes;
         this.start = start;
@@ -200,8 +204,8 @@ public class CacheIndexedObjectImpl extends CacheObjectAdapter {
             Object val = ctx.processor().unmarshal(ctx, valBytes, start, len,
                 ctx.kernalContext().config().getClassLoader());
 
-            if (ctx.storeValue())
-                this.val = val;
+            //if (ctx.storeValue())
+            //    this.val = val;
 
             return val;
         }
@@ -326,7 +330,8 @@ public class CacheIndexedObjectImpl extends CacheObjectAdapter {
      * @return {@code true} if detached.
      */
     protected boolean detached() {
-        return start == 0 && len == valBytes.length;
+        return true;
+        //return start == 0 && len == valBytes.length;
     }
 
     /**
@@ -343,6 +348,8 @@ public class CacheIndexedObjectImpl extends CacheObjectAdapter {
 
             start = 0;
             len = valBytes.length;
+
+            val = null;
         }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
index 5456fad..730b5d5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cacheobject/IgniteCacheObjectProcessorImpl.java
@@ -511,7 +511,7 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
             try {
                 toMarshaledFormIfNeeded(ctx);
 
-                if (ctx.storeValue()) {
+                /*if (ctx.storeValue()) {
                     ClassLoader ldr = ctx.p2pEnabled() ?
                         IgniteUtils.detectClass(this.val).getClassLoader() : val.getClass().getClassLoader();
 
@@ -519,7 +519,7 @@ public class IgniteCacheObjectProcessorImpl extends GridProcessorAdapter impleme
                         ctx.processor().unmarshal(ctx, valBytes, start, len, ldr);
 
                     return new CacheIndexedObjectImpl(val, valBytes, start, len);
-                }
+                }*/
 
                 return new CacheIndexedObjectImpl(null, valBytes, start, len);
             }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java b/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java
index 975fb68..4d06d81 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridDataInput.java
@@ -194,4 +194,13 @@ public interface GridDataInput extends DataInput {
      * @return Integer value.
      */
     public int readInt(int pos);
+
+    /**
+     * Fast read of long value at {@code pos}.
+     *
+     * @param pos Position in array.
+     *
+     * @return Long value.
+     */
+    public long readLong(int pos);
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java b/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java
index ed04cfe..5c15c4c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/io/GridUnsafeDataInput.java
@@ -511,6 +511,11 @@ public class GridUnsafeDataInput extends InputStream implements GridDataInput {
         return UNSAFE.getInt(buf, byteArrOff + pos);
     }
 
+    /** {@inheritDoc} */
+    @Override public long readLong(int pos) {
+        return UNSAFE.getLong(buf, byteArrOff + pos);
+    }
+
     /**
      * Reads in the "body" (i.e., the UTF representation minus the 2-byte
      * or 8-byte length header) of a UTF encoding, which occupies the next

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
index 9773d40..3596548 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedClassDescriptor.java
@@ -436,7 +436,6 @@ public class OptimizedClassDescriptor {
                                         fieldInfo = new FieldInfo(null,
                                             serField.getName(),
                                             -1,
-                                            resolveFieldId(serField.getName()),
                                             fieldType(serField.getType()));
                                     }
                                     else {
@@ -445,7 +444,6 @@ public class OptimizedClassDescriptor {
                                         fieldInfo = new FieldInfo(f,
                                             serField.getName(),
                                             UNSAFE.objectFieldOffset(f),
-                                            resolveFieldId(serField.getName()),
                                             fieldType(serField.getType()));
                                     }
 
@@ -471,7 +469,6 @@ public class OptimizedClassDescriptor {
                                     FieldInfo fieldInfo = new FieldInfo(f,
                                         f.getName(),
                                         UNSAFE.objectFieldOffset(f),
-                                        resolveFieldId(f.getName()),
                                         fieldType(f.getType()));
 
                                     clsFields.add(fieldInfo);
@@ -891,8 +888,6 @@ public class OptimizedClassDescriptor {
         /** Field name. */
         private final String fieldName;
 
-        /** ID calculated from field's name. */
-        private final int fieldId;
 
         /**
          * @param field Field.
@@ -900,19 +895,11 @@ public class OptimizedClassDescriptor {
          * @param offset Field offset.
          * @param type Grid optimized field type.
          */
-        FieldInfo(Field field, String name, long offset, int fieldId, OptimizedFieldType type) {
+        FieldInfo(Field field, String name, long offset, OptimizedFieldType type) {
             this.field = field;
             fieldOffs = offset;
             fieldType = type;
             fieldName = name;
-            this.fieldId = fieldId;
-        }
-
-        /**
-         * @return Field ID.
-         */
-        public int id() {
-            return fieldId;
         }
 
         /**

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshalAwareMetaCollector.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshalAwareMetaCollector.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshalAwareMetaCollector.java
index c4fb9d4..79c5f24 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshalAwareMetaCollector.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshalAwareMetaCollector.java
@@ -174,6 +174,6 @@ class OptimizedMarshalAwareMetaCollector implements OptimizedFieldsWriter {
      * @param type Field type.
      */
     private void putFieldToMeta(String fieldName, OptimizedFieldType type) {
-        meta.addMeta(OptimizedMarshallerUtils.resolveFieldId(fieldName), type);
+        meta.addField(fieldName, type);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExt.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExt.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExt.java
index fd0fa47..91e5a9a 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExt.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExt.java
@@ -40,16 +40,16 @@ public class OptimizedMarshallerExt extends OptimizedMarshaller {
     static final byte FOOTER_LEN_OFF = 2;
 
     /** */
-    static final int FOOTER_BODY_LEN_MASK = 0x3FFFFFFF;
+    static final byte FOOTER_HANDLES_FLAG_OFF = 3;
 
     /** */
-    static final int FOOTER_BODY_IS_HANDLE_MASK = 0x40000000;
+    static final int FOOTER_BODY_OFF_MASK = 0x3FFFFFFF;
 
     /** */
-    static final byte FOOTER_BODY_HANDLE_MASK_BIT = 30;
+    static final int FOOTER_BODY_IS_HANDLE_MASK = 0x40000000;
 
     /** */
-    public static final byte VARIABLE_LEN = -1;
+    static final byte FOOTER_BODY_HANDLE_MASK_BIT = 30;
 
     /** */
     private final static ConcurrentHashMap<Class<?>, Boolean> indexingEnabledCache = new ConcurrentHashMap<>();
@@ -173,7 +173,7 @@ public class OptimizedMarshallerExt extends OptimizedMarshaller {
 
                     for (ClassFields clsFields : desc.fields().fieldsList())
                         for (FieldInfo info : clsFields.fieldInfoList())
-                            meta.addMeta(info.id(), info.type());
+                            meta.addField(info.name(), info.type());
 
                     metaHandler.addMeta(desc.typeId(), meta);
 

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
index 6e7d7d6..5a35586 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectInputStream.java
@@ -500,15 +500,15 @@ public class OptimizedObjectInputStream extends ObjectInputStream implements Opt
      * @throws ClassNotFoundException In case of error.
      * @throws IOException In case of error.
      */
-    HashMap<Integer, Object> readFields( OptimizedObjectMetadata meta) throws ClassNotFoundException,
+    HashMap<Integer, Object> readFields(OptimizedObjectMetadata meta) throws ClassNotFoundException,
         IOException {
 
         HashMap<Integer, Object> fieldsValues = new HashMap<>();
 
-        for (OptimizedObjectMetadata.FieldInfo fieldInfo : meta.getMeta()) {
+        for (Map.Entry<Integer, OptimizedObjectMetadata.FieldInfo> entry : meta.metaList()) {
             Object val;
 
-            switch ((fieldInfo.type())) {
+            switch ((entry.getValue().type())) {
                 case BYTE:
                     readFieldType();
 
@@ -572,10 +572,10 @@ public class OptimizedObjectInputStream extends ObjectInputStream implements Opt
                     break;
 
                 default:
-                    throw new IgniteException("Unknown field type: " + fieldInfo.type());
+                    throw new IgniteException("Unknown field type: " + entry.getValue().type());
             }
 
-            fieldsValues.put(fieldInfo.id(), val);
+            fieldsValues.put(entry.getKey(), val);
         }
 
         return fieldsValues;
@@ -1315,7 +1315,15 @@ public class OptimizedObjectInputStream extends ObjectInputStream implements Opt
         if (type != SERIALIZABLE && type != MARSHAL_AWARE)
             return false;
 
-        FieldRange range = fieldRange(fieldName, start);
+        FieldRange range;
+
+        try {
+            range = fieldRange(fieldName, start);
+        }
+        catch (IgniteFieldNotFoundException e) {
+            // Ignore
+            return false;
+        }
 
         return range != null && range.start >= 0;
     }
@@ -1368,17 +1376,14 @@ public class OptimizedObjectInputStream extends ObjectInputStream implements Opt
      * @param start Object's start offset.
      * @return positive range or {@code null} if the object doesn't have such a field.
      * @throws IOException in case of error.
+     * @throws IgniteFieldNotFoundException In case if there is no such a field.
      */
-    private FieldRange fieldRange(String fieldName, int start) throws IOException {
+    private FieldRange fieldRange(String fieldName, int start) throws IOException, IgniteFieldNotFoundException {
         int pos = start + 1;
 
-        int fieldId = resolveFieldId(fieldName);
-
         int typeId = in.readInt(pos);
         pos += 4;
 
-        int clsNameLen = 0;
-
         if (typeId == 0) {
             int oldPos = in.position();
 
@@ -1386,8 +1391,6 @@ public class OptimizedObjectInputStream extends ObjectInputStream implements Opt
 
             typeId = OptimizedMarshallerUtils.resolveTypeId(readUTF(), mapper);
 
-            clsNameLen = in.position() - pos;
-
             in.position(oldPos);
         }
 
@@ -1402,58 +1405,58 @@ public class OptimizedObjectInputStream extends ObjectInputStream implements Opt
         short footerLen = in.readShort(end - FOOTER_LEN_OFF);
 
         if (footerLen == EMPTY_FOOTER)
-            return null;
+            throw new IgniteFieldNotFoundException("Object doesn't have a field named: " + fieldName);
+
+        if (range == null)
+            range = new FieldRange();
 
-        // Calculating footer offset. +2 - skipping length at the beginning
+        // Calculating start footer offset. +2 - skipping length at the beginning
         pos = (end - footerLen) + 2;
 
-        int fieldOff = 0;
+        int fieldIdx = meta.fieldIndex(fieldName);
+        int fieldsCnt = meta.size();
 
-        for (OptimizedObjectMetadata.FieldInfo info : meta.getMeta()) {
-            int len;
-            boolean isHandle;
+        if (fieldIdx >= fieldsCnt)
+            throw new IOException("Wrong field index for field name [idx=" + fieldIdx + ", name=" + fieldName + "]");
 
-            if (info.length() == VARIABLE_LEN) {
-                int fieldInfo = in.readInt(pos);
+        boolean hasHandles = in.readByte(end - FOOTER_HANDLES_FLAG_OFF) == 1;
 
-                pos += 4;
-                len = fieldInfo & FOOTER_BODY_LEN_MASK;
-                isHandle = ((fieldInfo & FOOTER_BODY_IS_HANDLE_MASK) >> FOOTER_BODY_HANDLE_MASK_BIT) == 1;
-            }
-            else {
-                len = info.length();
-                isHandle = false;
-            }
+        if (hasHandles) {
+            long fieldInfo = in.readLong(pos + fieldIdx * 8);
 
-            if (info.id() == fieldId) {
-                if (range == null)
-                    range = new FieldRange();
+            boolean isHandle = ((fieldInfo & FOOTER_BODY_IS_HANDLE_MASK) >> FOOTER_BODY_HANDLE_MASK_BIT) == 1;
 
-                if (!isHandle) {
-                    //object header len: 1 - for type, 4 - for type ID, 2 - for checksum.
-                    fieldOff += 1 + 4 + clsNameLen + 2;
+            range.start = (int)(fieldInfo & FOOTER_BODY_OFF_MASK);
 
-                    range.start = start + fieldOff;
-                    range.len = len;
-                }
-                else {
-                    range.start = in.readInt(pos);
-                    range.len = in.readInt(pos + 4);
-                }
+            if (isHandle) {
+                range.len = (int)(fieldInfo >>> 32);
+
+                if (range.len == 0)
+                    // Field refers to its object.
+                    range.len = (end - start) - footerLen;
 
                 return range;
             }
-            else {
-                fieldOff += len;
+        }
+        else
+            range.start = in.readInt(pos + fieldIdx * 4) & FOOTER_BODY_OFF_MASK;
 
-                if (isHandle) {
-                    pos += 8;
-                    fieldOff += 8;
-                }
+        if (fieldIdx == 0) {
+            if (fieldsCnt > 1) {
+                int nextFieldOff = in.readInt(pos + (fieldIdx + 1) * 4);
+                range.len = nextFieldOff - range.start;
             }
+            else
+                range.len = (end - footerLen) - range.start;
+        }
+        else if (fieldIdx == fieldsCnt - 1)
+            range.len = (end - footerLen) - range.start;
+        else {
+            int nextFieldOff = in.readInt(pos + (fieldIdx + 1) * 4);
+            range.len = nextFieldOff - range.start;
         }
 
-        return null;
+        return range;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadata.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadata.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadata.java
index 97facca..9e5a18a 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadata.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectMetadata.java
@@ -17,21 +17,21 @@
 
 package org.apache.ignite.marshaller.optimized;
 
-import org.apache.ignite.*;
-
 import java.io.*;
 import java.util.*;
 
 /**
- * Metadata that keeps fields information. Used in conjunction with the footer that is added to some objects during
- * marshalling.
+ * Metadata that keeps fields' name to id mapping.
  */
 public class OptimizedObjectMetadata implements Externalizable {
     /** */
     private static final long serialVersionUID = 0L;
 
     /** */
-    private List<FieldInfo> fieldsInfo;
+    private LinkedHashMap<Integer, FieldInfo> indexes;
+
+    /** */
+    private short counter;
 
     /** Constructor. */
     public OptimizedObjectMetadata() {
@@ -39,143 +39,92 @@ public class OptimizedObjectMetadata implements Externalizable {
     }
 
     /**
-     * Adds meta for a new field.
+     * Adds field to metadata list assigning an index to it that is used to locate field bucket in the footer of
+     * object's serialized form.
+     *
+     * @param fieldName Field name.
+     */
+    public void addField(String fieldName, OptimizedFieldType type) {
+        if (indexes == null)
+            indexes = new LinkedHashMap<>();
+
+        indexes.put(OptimizedMarshallerUtils.resolveFieldId(fieldName), new FieldInfo(type, counter++));
+    }
+
+    /**
+     * Returns an index assigned to the field. The index is used to locate field bucket in the footer of
+     * object's serialized form.
      *
-     * @param fieldId Field ID.
-     * @param fieldType Field type.
+     * @param fieldName Field name.
+     * @return Index.
+     * @throws IgniteFieldNotFoundException If object doesn't have such a field.
      */
-    public void addMeta(int fieldId, OptimizedFieldType fieldType) {
-        if (fieldsInfo == null)
-            fieldsInfo = new ArrayList<>();
+    public int fieldIndex(String fieldName) throws IgniteFieldNotFoundException {
+        if (indexes == null)
+            throw new IgniteFieldNotFoundException("Object doesn't have field named: " + fieldName);
 
+        FieldInfo info = indexes.get(OptimizedMarshallerUtils.resolveFieldId(fieldName));
 
+        if (info == null)
+            throw new IgniteFieldNotFoundException("Object doesn't have field named: " + fieldName);
 
-        fieldsInfo.add(new FieldInfo(fieldId, fieldType));
+        return info.index();
     }
 
     /**
-     * Gets {@link OptimizedObjectMetadata.FieldInfo} at the {@code index}.
+     * Returns meta list.
      *
-     * @param index Position.
-     * @return Field meta info.
+     * @return Meta list.
      */
-    public FieldInfo getMeta(int index) {
-        return fieldsInfo.get(index);
+    public Set<Map.Entry<Integer, FieldInfo>> metaList() {
+        return indexes.entrySet();
     }
+
     /**
-     * Returns all the metadata stored for the object.
+     * Returns number of fields.
      *
-     * @return Metadata collection.
+     * @return Number of fields.
      */
-    public List<FieldInfo> getMeta() {
-        return Collections.unmodifiableList(fieldsInfo);
+    public int size() {
+        return indexes.size();
     }
 
     /** {@inheritDoc} */
     @Override public void writeExternal(ObjectOutput out) throws IOException {
-        if (fieldsInfo == null) {
-            out.writeInt(0);
-            return;
-        }
-
-        out.writeInt(fieldsInfo.size());
-
-        for (FieldInfo fieldInfo : fieldsInfo) {
-            out.writeInt(fieldInfo.id);
-            out.writeByte(fieldInfo.type.ordinal());
-        }
+        out.writeObject(indexes);
     }
 
     /** {@inheritDoc} */
     @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
-        int size = in.readInt();
-
-        fieldsInfo = new ArrayList<>(size);
-
-        for (int i = 0; i < size; i++)
-            fieldsInfo.add(new FieldInfo(in.readInt(), OptimizedFieldType.values()[in.readByte()]));
+        indexes = (LinkedHashMap<Integer, FieldInfo>)in.readObject();
     }
 
     /**
      * Field info.
      */
-    public static class FieldInfo {
-        /** Field ID. */
-        private int id;
-
-        /** Field len. */
-        private int len;
-
-        /** Field type. */
+    public static class FieldInfo implements Serializable {
+        /** */
         private OptimizedFieldType type;
 
+        /** */
+        private short index;
+
         /**
          * Constructor.
-         *
-         * @param id Field ID.
-         * @param type Field len.
+         * @param type
+         * @param index
          */
-        public FieldInfo(int id, OptimizedFieldType type) {
-            this.id = id;
+        public FieldInfo(OptimizedFieldType type, short index) {
             this.type = type;
-
-            len = 1;
-
-            switch (type) {
-                case BYTE:
-                case BOOLEAN:
-                    len += 1;
-                    break;
-
-                case SHORT:
-                case CHAR:
-                    len += 2;
-                    break;
-
-                case INT:
-                case FLOAT:
-                    len += 4;
-                    break;
-
-                case LONG:
-                case DOUBLE:
-                    len += 8;
-                    break;
-
-                case OTHER:
-                    len = OptimizedMarshallerExt.VARIABLE_LEN;
-                    break;
-
-                default:
-                    throw new IgniteException("Unknown field type: " + type);
-            }
-
-            assert len != 1;
+            this.index = index;
         }
 
-        /**
-         * Returns ID.
-         * @return ID.
-         */
-        public int id() {
-            return id;
-        }
-
-        /**
-         * Returns length.
-         * @return Lenght.
-         */
-        public int length() {
-            return len;
-        }
-
-        /**
-         * Returns field type.
-         *
-         * @return Field type.
-         */
         public OptimizedFieldType type() {
             return type;
         }
+
+        public short index() {
+            return index;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
index 2bb71e9..c697c2e 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStream.java
@@ -538,12 +538,12 @@ public class OptimizedObjectOutputStream extends ObjectOutputStream implements O
     @SuppressWarnings("ForLoopReplaceableByForEach")
     private void writeFields(Object obj, OptimizedClassDescriptor.ClassFields fields, Footer footer)
         throws IOException {
-        int size;
+        int off;
 
         for (int i = 0; i < fields.size(); i++) {
             OptimizedClassDescriptor.FieldInfo t = fields.get(i);
 
-            size = out.size();
+            off = out.offset();
 
             switch (t.type()) {
                 case BYTE:
@@ -631,17 +631,14 @@ public class OptimizedObjectOutputStream extends ObjectOutputStream implements O
                         GridHandleTable.ObjectInfo objInfo = writeObject0(getObject(obj, t.offset()));
 
                         if (footer != null && objInfo != null) {
-                            footer.putHandle(t.id(), objInfo);
+                            footer.addNextHandleField(objInfo.position(), objInfo.length());
                             continue;
                         }
                     }
             }
 
-            if (footer != null && t.field() != null) {
-                int fieldLen = out.size() - size;
-
-                footer.put(t.id(), t.type(), fieldLen);
-            }
+            if (footer != null && t.field() != null)
+                footer.addNextFieldOff(off);
         }
     }
 
@@ -832,13 +829,13 @@ public class OptimizedObjectOutputStream extends ObjectOutputStream implements O
         if (curPut == null)
             throw new NotActiveException("putFields() was not called.");
 
-        int size;
+        int off;
 
         Footer footer = curPut.curFooter;
 
         for (IgniteBiTuple<OptimizedClassDescriptor.FieldInfo, Object> t : curPut.objs) {
 
-            size = out.size();
+            off = out.offset();
 
             switch (t.get1().type()) {
                 case BYTE:
@@ -909,16 +906,13 @@ public class OptimizedObjectOutputStream extends ObjectOutputStream implements O
                     GridHandleTable.ObjectInfo objInfo = writeObject0(t.get2());
 
                     if (footer != null && objInfo != null) {
-                        footer.putHandle(t.get1().id(), objInfo);
+                        footer.addNextHandleField(objInfo.position(), objInfo.length());
                         continue;
                     }
             }
 
-            if (footer != null) {
-                int fieldLen = out.size() - size;
-
-                footer.put(t.get1().id(), t.get1().type(), fieldLen);
-            }
+            if (footer != null)
+                footer.addNextFieldOff(off);
         }
     }
 
@@ -955,58 +949,58 @@ public class OptimizedObjectOutputStream extends ObjectOutputStream implements O
 
     /** {@inheritDoc} */
     @Override public void writeByte(String fieldName, byte val) throws IOException {
+        putFieldOffsetToFooter(out.offset());
         writeFieldType(BYTE);
         out.writeByte(val);
-        putFieldToFooter(fieldName, OptimizedFieldType.BYTE, 1);
     }
 
     /** {@inheritDoc} */
     @Override public void writeShort(String fieldName, short val) throws IOException {
+        putFieldOffsetToFooter(out.offset());
         writeFieldType(SHORT);
         out.writeShort(val);
-        putFieldToFooter(fieldName, OptimizedFieldType.SHORT, 2);
     }
 
     /** {@inheritDoc} */
     @Override public void writeInt(String fieldName, int val) throws IOException {
+        putFieldOffsetToFooter(out.offset());
         writeFieldType(INT);
         out.writeInt(val);
-        putFieldToFooter(fieldName, OptimizedFieldType.INT, 4);
     }
 
     /** {@inheritDoc} */
     @Override public void writeLong(String fieldName, long val) throws IOException {
+        putFieldOffsetToFooter(out.offset());
         writeFieldType(LONG);
         out.writeLong(val);
-        putFieldToFooter(fieldName, OptimizedFieldType.LONG, 8);
     }
 
     /** {@inheritDoc} */
     @Override public void writeFloat(String fieldName, float val) throws IOException {
+        putFieldOffsetToFooter(out.offset());
         writeFieldType(FLOAT);
         out.writeFloat(val);
-        putFieldToFooter(fieldName, OptimizedFieldType.FLOAT, 4);
     }
 
     /** {@inheritDoc} */
     @Override public void writeDouble(String fieldName, double val) throws IOException {
+        putFieldOffsetToFooter(out.offset());
         writeFieldType(DOUBLE);
         out.writeDouble(val);
-        putFieldToFooter(fieldName, OptimizedFieldType.DOUBLE, 8);
     }
 
     /** {@inheritDoc} */
     @Override public void writeChar(String fieldName, char val) throws IOException {
+        putFieldOffsetToFooter(out.offset());
         writeFieldType(CHAR);
         out.writeChar(val);
-        putFieldToFooter(fieldName, OptimizedFieldType.CHAR, 2);
     }
 
     /** {@inheritDoc} */
     @Override public void writeBoolean(String fieldName, boolean val) throws IOException {
+        putFieldOffsetToFooter(out.offset());
         writeFieldType(BOOLEAN);
         out.writeBoolean(val);
-        putFieldToFooter(fieldName, OptimizedFieldType.BOOLEAN, 1);
     }
 
     /** {@inheritDoc} */
@@ -1016,10 +1010,8 @@ public class OptimizedObjectOutputStream extends ObjectOutputStream implements O
 
     /** {@inheritDoc} */
     @Override public void writeObject(String fieldName, @Nullable Object obj) throws IOException {
-        int pos = out.offset();
-
+        putFieldOffsetToFooter(out.offset());
         writeObject(obj);
-        putFieldToFooter(fieldName, OptimizedFieldType.OTHER, out.offset() - pos);
     }
 
     /** {@inheritDoc} */
@@ -1093,14 +1085,12 @@ public class OptimizedObjectOutputStream extends ObjectOutputStream implements O
     }
 
     /**
-     * Puts field to the footer.
+     * Puts field's offset to the footer.
      *
-     * @param fieldName Field name.
-     * @param type Field type.
-     * @param len Field length.
+     * @param off Field offset.
      */
-    private void putFieldToFooter(String fieldName, OptimizedFieldType type, int len) {
-        marshalAwareFooters.peek().put(OptimizedMarshallerUtils.resolveFieldId(fieldName), type, len);
+    private void putFieldOffsetToFooter(int off) {
+        marshalAwareFooters.peek().addNextFieldOff(off);
     }
 
     /**
@@ -1230,22 +1220,19 @@ public class OptimizedObjectOutputStream extends ObjectOutputStream implements O
         void indexingSupported(boolean indexingSupported);
 
         /**
-         * Puts type ID and its value len to the footer.
+         * Adds offset of a field that must be placed next to the footer.
          *
-         * @param fieldId   Field ID.
-         * @param fieldType Field type.
-         * @param len       Total number of bytes occupied by type's value.
+         * @param off Field offset.
          */
-        void put(int fieldId, OptimizedFieldType fieldType, int len);
-
+        void addNextFieldOff(int off);
 
         /**
-         * Puts handle ID for the given field ID.
+         * Adds handle's offset of a field that must be placed next to the footer.
          *
-         * @param fieldId Field ID.
-         * @param objInfo Object's, referred by handle, info.
+         * @param handleOff Handle offset.
+         * @param handleLen Handle length.
          */
-        void putHandle(int fieldId, GridHandleTable.ObjectInfo objInfo);
+        void addNextHandleField(int handleOff, int handleLen);
 
         /**
          * Writes footer content to the OutputStream.

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStreamExt.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStreamExt.java b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStreamExt.java
index 9833e98..ad5b515 100644
--- a/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStreamExt.java
+++ b/modules/core/src/main/java/org/apache/ignite/marshaller/optimized/OptimizedObjectOutputStreamExt.java
@@ -58,10 +58,7 @@ public class OptimizedObjectOutputStreamExt extends OptimizedObjectOutputStream
      */
     private class FooterImpl implements OptimizedObjectOutputStream.Footer {
         /** */
-        private ArrayList<Meta> data;
-
-        /** */
-        private HashMap<Integer, GridHandleTable.ObjectInfo> handles;
+        private ArrayList<Long> data;
 
         /** */
         private boolean hasHandles;
@@ -75,28 +72,15 @@ public class OptimizedObjectOutputStreamExt extends OptimizedObjectOutputStream
         }
 
         /** {@inheritDoc} */
-        @Override public void put(int fieldId, OptimizedFieldType fieldType, int len) {
-            if (data == null)
-                return;
-
-            if (fieldType == OptimizedFieldType.OTHER)
-                data.add(new Meta(fieldId, len));
+        @Override public void addNextFieldOff(int off) {
+            data.add((long)(off & ~FOOTER_BODY_IS_HANDLE_MASK));
         }
 
         /** {@inheritDoc} */
-        @Override public void putHandle(int fieldId, GridHandleTable.ObjectInfo objInfo) {
-            if (data == null)
-                return;
+        @Override public void addNextHandleField(int handleOff, int handleLength) {
+            hasHandles = true;
 
-            if (!hasHandles) {
-                hasHandles = true;
-                handles = new HashMap<>();
-            }
-
-            handles.put(fieldId, objInfo);
-
-            // length of handle fields is 5 bytes.
-            put(fieldId, OptimizedFieldType.OTHER, 5);
+            data.add(((long)handleLength << 32) | (handleOff | FOOTER_BODY_IS_HANDLE_MASK));
         }
 
         /** {@inheritDoc} */
@@ -104,61 +88,25 @@ public class OptimizedObjectOutputStreamExt extends OptimizedObjectOutputStream
             if (data == null)
                 writeShort(EMPTY_FOOTER);
             else {
-                int bodyEnd = out.offset();
-
-                // +4 - 2 bytes for footer len at the beginning, 2 bytes for footer len at the end.
-                short footerLen = (short)(data.size() * 4 + 4);
-
-                if (hasHandles)
-                    footerLen += handles.size() * 8;
+                // +5 - 2 bytes for footer len at the beginning, 2 bytes for footer len at the end, 1 byte for handles
+                // indicator flag.
+                short footerLen = (short)(data.size() * (hasHandles ? 8 : 4) + 5);
 
                 writeShort(footerLen);
 
                 if (hasHandles) {
-                    for (Meta fieldMeta : data) {
-                        GridHandleTable.ObjectInfo objInfo = handles.get(fieldMeta.fieldId);
-
-                        if (objInfo == null)
-                            writeInt(fieldMeta.fieldLen & ~FOOTER_BODY_IS_HANDLE_MASK);
-                        else {
-                            writeInt(fieldMeta.fieldLen | FOOTER_BODY_IS_HANDLE_MASK);
-                            writeInt(objInfo.position());
-
-                            if (objInfo.length() == 0)
-                                // field refers to its own object that hasn't set total length yet.
-                                writeInt((bodyEnd - objInfo.position()) + footerLen);
-                            else
-                                writeInt(objInfo.length());
-                        }
-                    }
+                    for (long body : data)
+                        writeLong(body);
+                }
+                else {
+                    for (long body : data)
+                        writeInt((int)body);
                 }
-                else
-                    for (Meta fieldMeta : data)
-                        // writing field len and resetting is handle mask
-                        writeInt(fieldMeta.fieldLen & ~FOOTER_BODY_IS_HANDLE_MASK);
+
+                writeByte(hasHandles ? 1 : 0);
 
                 writeShort(footerLen);
             }
         }
     }
-
-    /**
-     *
-     */
-    private static class Meta {
-        /** */
-        int fieldId;
-
-        /** */
-        int fieldLen;
-
-        /**
-         * @param fieldId
-         * @param fieldLen
-         */
-        public Meta(int fieldId, int fieldLen) {
-            this.fieldId = fieldId;
-            this.fieldLen = fieldLen;
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2e8bdec6/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExtSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExtSelfTest.java b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExtSelfTest.java
index 0e9fb61..94a7523 100644
--- a/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExtSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/marshaller/optimized/OptimizedMarshallerExtSelfTest.java
@@ -22,6 +22,7 @@ import org.apache.ignite.marshaller.*;
 import org.apache.ignite.testframework.junits.common.*;
 
 import java.io.*;
+import java.lang.reflect.*;
 import java.util.*;
 import java.util.concurrent.*;
 
@@ -106,11 +107,19 @@ public class OptimizedMarshallerExtSelfTest extends OptimizedMarshallerSelfTest
         // Must be returned in a wrapped form, since metadata was added enabling the footer.
         CacheIndexedObjectImpl cacheObject = marsh.readField("o2", arr, 0, arr.length, null);
 
+
         arr = cacheObject.valueBytes(null);
 
+        Field start = cacheObject.getClass().getDeclaredField("start");
+        start.setAccessible(true);
+
+        Field len = cacheObject.getClass().getDeclaredField("len");
+        len.setAccessible(true);
+
         // Check enclosed objects fields
-        assertTrue(marsh.hasField("i", arr, 0, arr.length));
-        assertEquals(testObj.o2.i, (int)marsh.readField("i", arr, 0, arr.length, null));
+        assertTrue(marsh.hasField("i", arr, start.getInt(cacheObject), len.getInt(cacheObject)));
+        assertEquals(testObj.o2.i, (int)marsh.readField("i", arr, start.getInt(cacheObject), len.getInt(cacheObject),
+            null));
     }
 
     /**
@@ -174,14 +183,26 @@ public class OptimizedMarshallerExtSelfTest extends OptimizedMarshallerSelfTest
         CacheIndexedObjectImpl cacheObj = marsh.readField("aware", arr, 0, arr.length, null);
         byte[] cacheObjArr = cacheObj.valueBytes(null);
 
-        Date date = marsh.readField("date", cacheObjArr, 0, cacheObjArr.length, null);
+        Field start = cacheObj.getClass().getDeclaredField("start");
+        start.setAccessible(true);
+
+        Field len = cacheObj.getClass().getDeclaredField("len");
+        len.setAccessible(true);
+
+        Date date = marsh.readField("date", cacheObjArr, start.getInt(cacheObj), len.getInt(cacheObj), null);
 
         assertEquals(test.aware.date, date);
 
         cacheObj = marsh.readField("testObject2", arr, 0, arr.length, null);
         cacheObjArr = cacheObj.valueBytes(null);
 
-        int n = marsh.readField("i", cacheObjArr, 0, cacheObjArr.length, null);
+        start = cacheObj.getClass().getDeclaredField("start");
+        start.setAccessible(true);
+
+        len = cacheObj.getClass().getDeclaredField("len");
+        len.setAccessible(true);
+
+        int n = marsh.readField("i", cacheObjArr, start.getInt(cacheObj), len.getInt(cacheObj), null);
 
         assertEquals(test.testObject2.i, n);