[PATCH] 7901019: jol estimates do not take AllocationStyle into the account

Тимур Гибадуллин timur.gbd at gmail.com
Sun Jul 3 13:04:11 UTC 2016


Added takeHierarchyGaps, takeSuperGaps options handling into the 
Hotspotlayouter, tests still passes.

On 03.07.16 1:57, Тимур Гибадуллин wrote:
> I haven't mentioned, currently Hotspotlayouter does not handle 
> takeHierarchyGaps and takeSuperGaps while lauouting. I will add them 
> very soon.
>
> On 02.07.16 2:14, Тимур Гибадуллин wrote:
>> Thanks! Fixed this, have changed the layouting algorithm to working 
>> on fields of each class in isolation, also added proper handling of 
>> the second allocation style.
>>
>>
>> On 06.06.16 19:22, Aleksey Shipilev wrote:
>>> Hi,
>>>
>>> On 06/06/2016 12:47 AM, Тимур Гибадуллин wrote:
>>>> Hey there! I fixed tests, there were several bugs during determining
>>>> allocation types of java types, also I removed autoAlign option from
>>>> HotspotLayouter, because it seems, that now it is unnecessary, 
>>>> since we
>>>> emulate original Hotspot algorithm of layouting.
>>> Actually, autoAlign is needed to allow layout simulation (same with
>>> take{Hierarchy|Super}Gaps). HotspotLayouter in default mode should 
>>> match
>>> the current VM layout precisely.
>>>
>>> I have corrected a few things in the patch, and added a new tests that
>>> matches the actual VM layout against HotspotLayouter:
>>> http://cr.openjdk.java.net/~shade/7901019/AllocationStyle-v2.patch
>>>
>>> This identified at least one bug: new code takes all fields from the
>>> entire hierarchy, and lays them out. This is not how a real layouter
>>> works: it works on fields of each class in isolation.
>>>
>>> Actual layout:
>>>
>>> Class9943 object internals:
>>>   OFFSET  SIZE    TYPE DESCRIPTION                    VALUE
>>>        0    12         (object header)                N/A
>>>       12     1 boolean Class9942.field0               N/A
>>>       13     3         (alignment/padding gap)        N/A
>>>       16     8  double Class9943.field0               N/A
>>>       24     2   short Class9943.field1               N/A
>>>       26     6         (loss due to the next object alignment)
>>> Instance size: 32 bytes
>>> Space losses: 3 bytes internal + 6 bytes external = 9 bytes total
>>>
>>> VM Layout Simulation (current, field allocation style: 2)
>>> Class9943 object internals:
>>>   OFFSET  SIZE    TYPE DESCRIPTION                    VALUE
>>>        0    12         (object header)                N/A
>>>       12     4         (alignment/padding gap)        N/A
>>>       16     8  double Class9943.field0               N/A
>>>       24     2   short Class9943.field1               N/A
>>>       26     1 boolean Class9942.field0               N/A
>>>       27     5         (loss due to the next object alignment)
>>> Instance size: 32 bytes
>>> Space losses: 4 bytes internal + 5 bytes external = 9 bytes total
>>>
>>> Notice how Class9942 field is laid out after all field in patched
>>> HotspotLayouter. This is incorrect. Once you fix that,
>>> take{Hierarchy|Super}Gaps gain meaning: hierarchy gap is a gap due to
>>> "next class field block" alignment, and super gap is the hole left in
>>> superclass after layout, that may be used to put some subclass fields.
>>>
>>> Thanks,
>>> -Aleksey
>>>
>>
>

-------------- next part --------------
Index: jol-cli/src/main/java/org/openjdk/jol/operations/HeapDump.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-cli/src/main/java/org/openjdk/jol/operations/HeapDump.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-cli/src/main/java/org/openjdk/jol/operations/HeapDump.java	(revision 79+:e8e889a8ae3d+)
@@ -75,40 +75,25 @@
             long rawData = process(data, l);
             out.printf("%11s %,15d: %s%n", "", rawData, l);
 
-            l = new HotSpotLayouter(model, false, false, false);
+            l = new HotSpotLayouter(model);
             long hsBase = process(data, l);
             out.printf("%11s %,15d: %s%n", "", hsBase, l);
 
-            {
-                l = new HotSpotLayouter(model, true, false, false);
-                long s = process(data, l);
-                out.printf("%10.3f%% %,15d: %s%n", (s - hsBase) * 100.0 / hsBase, s, l);
-            }
+            final boolean[] BOOLS = {false, true};
 
-            {
-                l = new HotSpotLayouter(model, false, true, false);
+            for (boolean hierarchyGaps : BOOLS) {
+                for (boolean superClassGaps : BOOLS) {
+                    for (boolean autoAlign : BOOLS) {
+                        for (boolean compactFields : BOOLS) {
+                            for (int fieldAllocationStyle : new int[]{0, 1, 2}) {
+                                l = new HotSpotLayouter(model, hierarchyGaps, superClassGaps, autoAlign, compactFields, fieldAllocationStyle);
-                long s = process(data, l);
-                out.printf("%10.3f%% %,15d: %s%n", (s - hsBase) * 100.0 / hsBase, s, l);
-            }
+                                long s = process(data, l);
+                                out.printf("%10.3f%% %,15d: %s%n", (s - hsBase) * 100.0 / hsBase, s, l);
+                            }
-
-            {
-                l = new HotSpotLayouter(model, false, false, true);
-                long s = process(data, l);
-                out.printf("%10.3f%% %,15d: %s%n", (s - hsBase) * 100.0 / hsBase, s, l);
-            }
+                        }
-
-            {
-                l = new HotSpotLayouter(model, true, false, true);
-                long s = process(data, l);
-                out.printf("%10.3f%% %,15d: %s%n", (s - hsBase) * 100.0 / hsBase, s, l);
-            }
+                    }
-
-            {
-                l = new HotSpotLayouter(model, false, true, true);
-                long s = process(data, l);
-                out.printf("%10.3f%% %,15d: %s%n", (s - hsBase) * 100.0 / hsBase, s, l);
-            }
+                }
-
+            }
             out.println();
         }
     }
Index: jol-core/src/test/java/org/openjdk/jol/util/ClassGenerator.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-core/src/test/java/org/openjdk/jol/util/ClassGenerator.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-core/src/test/java/org/openjdk/jol/util/ClassGenerator.java	(revision 79+:e8e889a8ae3d+)
@@ -14,24 +14,22 @@
 
 public class ClassGenerator {
 
-    private static final int MAX_CLASSES_IN_HIERARCHY = 5;
-    private static final int MAX_FIELDS_PER_CLASS = 50;
     private static final int CLASSFILE_VERSION = 50;
 
     private static final AtomicInteger idx = new AtomicInteger();
 
-    public static Class<?> generate(Random r) throws Exception {
+    public static Class<?> generate(Random r, int maxHierarchyDepth, int maxFieldsPerClass) throws Exception {
         ByteClassLoader classLoader = new ByteClassLoader();
 
-        int numClasses = r.nextInt(MAX_CLASSES_IN_HIERARCHY);
+        int numClasses = r.nextInt(maxHierarchyDepth + 1);
         Class<?> sup = Object.class;
         for (int c = 0; c < numClasses; c++) {
-            sup = generate(r, sup, classLoader);
+            sup = generate(r, sup, classLoader, maxFieldsPerClass);
         }
         return sup;
     }
 
-    private static Class<?> generate(Random r, Class<?> superClass, ByteClassLoader classLoader) throws Exception {
+    private static Class<?> generate(Random r, Class<?> superClass, ByteClassLoader classLoader, int maxFieldsPerClass) throws Exception {
         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
 
         String name = "Class" + idx.incrementAndGet();
@@ -47,7 +45,7 @@
 
         Class<?>[] types = new Class[] { boolean.class, byte.class, short.class, char.class, int.class, float.class, long.class, double.class, Object.class };
 
-        int count = r.nextInt(MAX_FIELDS_PER_CLASS);
+        int count = r.nextInt(maxFieldsPerClass);
         for (int c = 0; c < count; c++) {
             Class<?> type = types[r.nextInt(types.length)];
             cw.visitField(ACC_PUBLIC, "field" + c, Type.getType(type).getDescriptor(), null, null);
@@ -81,4 +79,4 @@
         }
     }
 
-}
\ No newline at end of file
+}
Index: jol-core/src/test/java/org/openjdk/jol/layouters/HotspotLayouterRealTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-core/src/test/java/org/openjdk/jol/layouters/HotspotLayouterRealTest.java	(revision 79+:e8e889a8ae3d+)
+++ jol-core/src/test/java/org/openjdk/jol/layouters/HotspotLayouterRealTest.java	(revision 79+:e8e889a8ae3d+)
@@ -0,0 +1,75 @@
+package org.openjdk.jol.layouters;
+
+import junit.framework.Assert;
+import org.junit.Test;
+import org.openjdk.jol.datamodel.CurrentDataModel;
+import org.openjdk.jol.datamodel.DataModel;
+import org.openjdk.jol.datamodel.X86_32_DataModel;
+import org.openjdk.jol.info.ClassLayout;
+import org.openjdk.jol.util.ClassGenerator;
+
+import java.util.*;
+
+public class HotspotLayouterRealTest {
+
+    private static final boolean[] BOOLS = {false, true};
+    private static final DataModel[] MODELS = { new CurrentDataModel() };
+    private static final int ITERATIONS = 20000;
+
+    @Test
+    public void testSingleClass() throws Exception {
+        tryWith(1, 5);
+    }
+
+    @Test
+    public void testTwoClasses() throws Exception {
+        tryWith(2, 5);
+    }
+
+    public void tryWith(int hierarchyDepth, int fieldsPerClass) throws Exception {
+        Random seeder = new Random();
+        for (int c = 0; c < ITERATIONS; c++) {
+            int seed = seeder.nextInt();
+            Class<?> cl = ClassGenerator.generate(new Random(seed), hierarchyDepth, fieldsPerClass);
+
+            try {
+                ClassLayout actual = ClassLayout.parseClass(cl);
+
+                Map<Layouter, ClassLayout> candidates = candidateLayouts(cl);
+
+                if (!candidates.values().contains(actual)) {
+                    System.out.println(actual.toPrintable());
+                    for (Layouter l : candidates.keySet()) {
+                        System.out.println(l);
+                        System.out.println(candidates.get(l).toPrintable());
+                    }
+                    Assert.fail("Actual layout should have matched at least one model layout. Seed = " + seed);
+                }
+            } catch (Exception e) {
+                Assert.fail("Failed. Seed = " + seed);
+            }
+        }
+    }
+
+    private Map<Layouter, ClassLayout> candidateLayouts(Class<?> cl) {
+        Map<Layouter, ClassLayout> layouts = new HashMap<Layouter, ClassLayout>();
+        for (DataModel model : MODELS) {
+            for (boolean hierarchyGaps : BOOLS) {
+                for (boolean superClassGaps : BOOLS) {
+                    for (boolean autoAlign : BOOLS) {
+                        for (boolean compactFields : BOOLS) {
+                            for (int fieldAllocationStyle : new int[]{0, 1, 2}) {
+                                HotSpotLayouter layouter = new HotSpotLayouter(model,
+                                        hierarchyGaps, superClassGaps, autoAlign,
+                                        compactFields, fieldAllocationStyle);
+                                layouts.put(layouter, ClassLayout.parseClass(cl, layouter));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return layouts;
+    }
+
+}
Index: jol-core/src/main/java/org/openjdk/jol/info/ClassLayout.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-core/src/main/java/org/openjdk/jol/info/ClassLayout.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-core/src/main/java/org/openjdk/jol/info/ClassLayout.java	(revision 79+:e8e889a8ae3d+)
@@ -185,6 +185,36 @@
         return toPrintable(classData.instance());
     }
 
+    public String toPrintableSimple() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+
+        long nextFree = headerSize();
+
+        long interLoss = 0;
+        long exterLoss = 0;
+
+        for (FieldLayout f : fields()) {
+            if (f.offset() > nextFree) {
+                interLoss += (f.offset() - nextFree);
+            }
+
+            nextFree = f.offset() + f.size();
+        }
+
+        long sizeOf = (classData.instance() != null) ? VM.current().sizeOf(classData.instance()) : instanceSize();;
+
+        if (sizeOf != nextFree) {
+            exterLoss = sizeOf - nextFree;
+        }
+
+        pw.printf(classData.name() + "\t%d\t%d\t%d\t%d%n", sizeOf, interLoss, exterLoss, interLoss + exterLoss);
+
+        pw.close();
+
+        return sw.toString();
+    }
+
     /**
      * Produce printable stringly representation of class layout.
      * This method accepts instance to read the actual data from.
@@ -297,4 +327,24 @@
         return s;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ClassLayout that = (ClassLayout) o;
+
+        if (headerSize != that.headerSize) return false;
+        if (size != that.size) return false;
+        return fields.equals(that.fields);
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = fields.hashCode();
+        result = 31 * result + headerSize;
+        result = 31 * result + (int) (size ^ (size >>> 32));
+        return result;
+    }
 }
Index: jol-cli/src/main/java/org/openjdk/jol/operations/ObjectEstimates.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-cli/src/main/java/org/openjdk/jol/operations/ObjectEstimates.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-cli/src/main/java/org/openjdk/jol/operations/ObjectEstimates.java	(revision 79+:e8e889a8ae3d+)
@@ -50,16 +50,16 @@
     @Override
     protected void runWith(Class<?> klass) throws Exception {
         out.println("***** 32-bit VM: **********************************************************");
-        out.println(ClassLayout.parseClass(klass, new HotSpotLayouter(new X86_32_DataModel(), false, false, false)).toPrintable());
+        out.println(ClassLayout.parseClass(klass, new HotSpotLayouter(new X86_32_DataModel())).toPrintable());
 
         out.println("***** 64-bit VM: **********************************************************");
-        out.println(ClassLayout.parseClass(klass, new HotSpotLayouter(new X86_64_DataModel(), false, false, false)).toPrintable());
+        out.println(ClassLayout.parseClass(klass, new HotSpotLayouter(new X86_64_DataModel())).toPrintable());
 
         out.println("***** 64-bit VM, compressed references enabled: ***************************");
-        out.println(ClassLayout.parseClass(klass, new HotSpotLayouter(new X86_64_COOPS_DataModel(), false, false, false)).toPrintable());
+        out.println(ClassLayout.parseClass(klass, new HotSpotLayouter(new X86_64_COOPS_DataModel())).toPrintable());
 
         out.println("***** 64-bit VM, compressed references enabled, 16-byte align: ************");
-        out.println(ClassLayout.parseClass(klass, new HotSpotLayouter(new X86_64_COOPS_DataModel(16), false, false, false)).toPrintable());
+        out.println(ClassLayout.parseClass(klass, new HotSpotLayouter(new X86_64_COOPS_DataModel(16))).toPrintable());
     }
 
 }
Index: jol-samples/src/main/java/org/openjdk/jol/samples/JOLSample_26_Hotspot.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-samples/src/main/java/org/openjdk/jol/samples/JOLSample_26_Hotspot.java	(revision 79+:e8e889a8ae3d+)
+++ jol-samples/src/main/java/org/openjdk/jol/samples/JOLSample_26_Hotspot.java	(revision 79+:e8e889a8ae3d+)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014, Oracle America, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ *  * Neither the name of Oracle nor the names of its contributors may be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.openjdk.jol.samples;
+
+import org.openjdk.jol.info.ClassLayout;
+
+import org.openjdk.jol.datamodel.X86_32_DataModel;
+import org.openjdk.jol.datamodel.X86_64_COOPS_DataModel;
+import org.openjdk.jol.datamodel.X86_64_DataModel;
+import org.openjdk.jol.layouters.HotSpotLayouter;
+import org.openjdk.jol.layouters.CurrentLayouter;
+import org.openjdk.jol.layouters.Layouter;
+
+import static java.lang.System.out;
+
+/**
+ * @author Aleksey Shipilev
+ */
+public class JOLSample_26_Hotspot {
+
+    public static void main(String[] args) throws Exception {
+        Layouter l;
+
+        l = new HotSpotLayouter(new X86_32_DataModel());
+        System.out.println("***** " + l);
+        System.out.println(ClassLayout.parseClass(A.class, l).toPrintable());
+
+        l = new HotSpotLayouter(new X86_64_COOPS_DataModel());
+        System.out.println("***** " + l);
+        System.out.println(ClassLayout.parseClass(A.class, l).toPrintable());
+
+        l = new HotSpotLayouter(new X86_64_DataModel());
+        System.out.println("***** " + l);
+        System.out.println(ClassLayout.parseClass(A.class, l).toPrintable());
+    }
+
+    public static class A {
+        private long value;
+        public long[] padding = new long[5];
+        public Object[] o = new Object[1];
+        char c;
+    }
+}
Index: jol-core/src/main/java/org/openjdk/jol/info/ClassData.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-core/src/main/java/org/openjdk/jol/info/ClassData.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-core/src/main/java/org/openjdk/jol/info/ClassData.java	(revision 79+:e8e889a8ae3d+)
@@ -24,13 +24,17 @@
  */
 package org.openjdk.jol.info;
 
+import org.openjdk.jol.datamodel.DataModel;
+
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.EnumMap;
 import java.util.List;
+import sun.misc.Contended;
 
 /**
  * Holds the class data, without the layout information.
@@ -92,7 +96,14 @@
         }
 
         ClassData cd = new ClassData(o, klass.getName());
+        Class superKlass = klass.getSuperclass();
 
+        cd.isContended = klass.getAnnotation(Contended.class) != null;
+
+        if (superKlass != null) {
+            cd.addSuperClassData(klass.getSuperclass());
+        }
+
         do {
             for (Field f : klass.getDeclaredFields()) {
                 if (!Modifier.isStatic(f.getModifiers())) {
@@ -113,6 +124,8 @@
     private final String arrayComponentKlass;
     private final long length;
     private final boolean isArray;
+    private boolean isContended;
+    private ClassData superClass;
 
     /**
      * Constructs the empty ClassData, suited for regular class.
@@ -130,6 +143,8 @@
         this.arrayKlass = null;
         this.arrayComponentKlass = null;
         this.isArray = false;
+        this.superClass = null;
+        this.isContended = false;
     }
 
     /**
@@ -152,6 +167,8 @@
         this.classNames = null;
         this.length = length;
         this.isArray = true;
+        this.superClass = null;
+        this.isContended = false;
     }
 
     /**
@@ -164,6 +181,15 @@
     }
 
     /**
+     * Add the super-class data of the class.
+     *
+     * @param superClass super class
+     */
+    public void addSuperClassData(Class superClass) {
+        this.superClass = parseClass(superClass);
+    }
+
+    /**
      * Add the field data.
      *
      * @param fieldData the data to add
@@ -183,6 +209,58 @@
     }
 
     /**
+     * Get the fields' of the own fields.
+     *
+     * @return field data
+     */
+    public Collection<FieldData> ownFields() {
+        return fieldsFor(classNames.get(classNames.size() - 1));
+    }
+
+    /**
+     * Counts the total size of fields.
+     *
+     * @return fields size
+     */
+    public int fieldSize(DataModel model) {
+        int fieldSize = 0;
+
+        for (FieldData f : fields) {
+            fieldSize += model.sizeOf(f.typeClass());
+        }
+
+        return fieldSize;
+    }
+
+    /**
+     * Returns the count of the oops in th class
+     *
+     * @return oops count
+     */
+    public int oopsCount() {
+        int count = 0;
+
+        for (FieldData f : fields) {
+            String simpleName = f.typeClass();
+
+            if (
+                !simpleName.equals("boolean") &&
+                !simpleName.equals("byte") &&
+                !simpleName.equals("short") &&
+                !simpleName.equals("char") &&
+                !simpleName.equals("int") &&
+                !simpleName.equals("float") &&
+                !simpleName.equals("long") &&
+                !simpleName.equals("double")
+            ) {
+                count++;
+            }
+        }
+
+        return count;
+    }
+
+    /**
      * Get the fields' data for the given class.
      *
      * @param klass class name
@@ -224,6 +302,24 @@
      */
     public boolean isArray() {
         return isArray;
+    }
+
+    /**
+     * Get ClassData of the super-class.
+     *
+     * @return ClassData
+     */
+    public ClassData superClass() {
+        return superClass;
+    }
+
+    /**
+     * Does the class have @Contended annotation?
+     *
+     * @return true, if class has @Contended annotation; false otherwise
+     */
+    public boolean isContended() {
+        return isContended;
     }
 
     /**
Index: jol-core/src/test/java/org/openjdk/jol/layouters/LayouterInvariantsTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-core/src/test/java/org/openjdk/jol/layouters/LayouterInvariantsTest.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-core/src/test/java/org/openjdk/jol/layouters/LayouterInvariantsTest.java	(revision 79+:e8e889a8ae3d+)
@@ -20,7 +20,7 @@
         Random seeder = new Random();
         for (int c = 0; c < ITERATIONS; c++) {
             int seed = seeder.nextInt();
-            Class<?> cl = ClassGenerator.generate(new Random(seed));
+            Class<?> cl = ClassGenerator.generate(new Random(seed), 5, 50);
 
             for (DataModel model : MODELS) {
                 try {
@@ -37,7 +37,7 @@
         Random seeder = new Random();
         for (int c = 0; c < ITERATIONS; c++) {
             int seed = seeder.nextInt();
-            Class<?> cl = ClassGenerator.generate(new Random(seed));
+            Class<?> cl = ClassGenerator.generate(new Random(seed), 5, 50);
 
             try {
                 ClassLayout.parseClass(cl, new CurrentLayouter());
@@ -52,14 +52,21 @@
         Random seeder = new Random();
         for (int c = 0; c < ITERATIONS; c++) {
             int seed = seeder.nextInt();
-            Class<?> cl = ClassGenerator.generate(new Random(seed));
+            Class<?> cl = ClassGenerator.generate(new Random(seed), 5, 50);
 
             try {
                 for (DataModel model : MODELS) {
                     for (boolean hierarchyGaps : BOOLS) {
                         for (boolean superClassGaps : BOOLS) {
                             for (boolean autoAlign : BOOLS) {
-                                ClassLayout.parseClass(cl, new HotSpotLayouter(model, hierarchyGaps, superClassGaps, autoAlign));
+                                for (boolean compactFields : BOOLS) {
+                                    for (int fieldAllocationStyle : new int[]{0, 1, 2}) {
+                                        ClassLayout.parseClass(cl, new HotSpotLayouter(model,
+                                                hierarchyGaps, superClassGaps, autoAlign,
+                                                compactFields, fieldAllocationStyle)
+                                        );
+                                    }
+                                }
                             }
                         }
                     }
Index: jol-core/src/main/java/org/openjdk/jol/info/FieldData.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-core/src/main/java/org/openjdk/jol/info/FieldData.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-core/src/main/java/org/openjdk/jol/info/FieldData.java	(revision 79+:e8e889a8ae3d+)
@@ -28,6 +28,7 @@
 import org.openjdk.jol.vm.VM;
 
 import java.lang.reflect.Field;
+import sun.misc.Contended;
 
 /**
  * Holds the field information, without the layout.
@@ -45,7 +46,7 @@
      * @return field data
      */
     public static FieldData create(String hostKlass, String fieldName, String fieldType) {
-        return new FieldData(null, -1, hostKlass, fieldName, fieldType);
+        return new FieldData(null, -1, hostKlass, fieldName, fieldType, false, null);
     }
 
     /**
@@ -55,7 +56,17 @@
      * @return field data
      */
     public static FieldData parse(Field field) {
-        return new FieldData(field, VM.current().fieldOffset(field), field.getDeclaringClass().getSimpleName(), field.getName(), field.getType().getSimpleName());
+        Contended contentded = field.getAnnotation(Contended.class);
+        boolean isContended = contentded != null;
+        return new FieldData(
+            field,
+            VM.current().fieldOffset(field),
+            field.getDeclaringClass().getSimpleName(),
+            field.getName(),
+            field.getType().getSimpleName(),
+            isContended,
+            isContended ? contentded.value() : null
+        );
     }
 
     private final String name;
@@ -63,13 +74,17 @@
     private final String klass;
     private final Field refField;
     private final long vmOffset;
+    private final boolean isContended;
+    private final String contendedGroup;
 
-    private FieldData(Field refField, long vmOffset, String hostKlass, String fieldName, String fieldType) {
+    private FieldData(Field refField, long vmOffset, String hostKlass, String fieldName, String fieldType, boolean isContended, String contendedGroup) {
         this.klass = hostKlass;
         this.name = fieldName;
         this.type = fieldType;
         this.refField = refField;
         this.vmOffset = vmOffset;
+        this.isContended = isContended;
+        this.contendedGroup = contendedGroup;
     }
 
     /**
@@ -100,6 +115,33 @@
     }
 
     /**
+     * Answers whether the field has contentded annotation.
+     *
+     * @return true, if the field is contended
+     */
+    public boolean isContended() {
+        return isContended;
+    }
+
+    /**
+     * Get contentded group of the field.
+     *
+     * @return String
+     */
+    public String contendedGroup() {
+        return contendedGroup;
+    }
+
+    /**
+     * Get original Field.
+     *
+     * @return Field which is represented by the FieldData
+     */
+    public Field refField() {
+        return refField;
+    }
+
+    /**
      * Gets the string representation of field value,
      * if appropriate.
      *
@@ -139,4 +181,7 @@
         }
     }
 
+    public String toString() {
+        return refField.toString();
+    }
 }
Index: jol-cli/src/main/java/org/openjdk/jol/operations/StringCompress.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-cli/src/main/java/org/openjdk/jol/operations/StringCompress.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-cli/src/main/java/org/openjdk/jol/operations/StringCompress.java	(revision 79+:e8e889a8ae3d+)
@@ -202,7 +202,7 @@
                 }
             } else if (DO_MODE.equalsIgnoreCase("estimates")) {
                 for (DataModel model : DATA_MODELS) {
-                    printLine(data, new HotSpotLayouter(model, false, false, false));
+                    printLine(data, new HotSpotLayouter(model));
                 }
             }
 
Index: jol-core/src/main/java/org/openjdk/jol/layouters/HotSpotLayouter.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-core/src/main/java/org/openjdk/jol/layouters/HotSpotLayouter.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-core/src/main/java/org/openjdk/jol/layouters/HotSpotLayouter.java	(revision 79+:e8e889a8ae3d+)
@@ -31,32 +31,59 @@
 import org.openjdk.jol.info.FieldLayout;
 import org.openjdk.jol.util.MathUtil;
 
-import java.util.BitSet;
-import java.util.Collection;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
+import java.lang.IllegalStateException;
+import java.util.*;
 
+import static org.openjdk.jol.layouters.FieldAllocationType.*;
+
+
 /**
  * VM layout simulator.
  *
  * @author Aleksey Shipilev
  */
 public class HotSpotLayouter implements Layouter {
+    // The next classes have predefined hard-coded fields offsets.
+    private static HashSet<String> predefinedFieldsOffsetsClasses = new HashSet<String>(Arrays.asList(
+            "java.lang.AssertionStatusDirectives",
+            "java.lang.Class",
+            "java.lang.ClassLoader",
+            "java.lang.ref.Reference",
+            "java.lang.ref.SoftReference",
+            "java.lang.StackTraceElement",
+            "java.lang.String",
+            "java.lang.Throwable",
+            "java.lang.Boolean",
+            "java.lang.Character",
+            "java.lang.Float",
+            "java.lang.Double",
+            "java.lang.Byte",
+            "java.lang.Short",
+            "java.lang.Integer",
+            "java.lang.Long"
+    ));
+
+    static final int CONTENDED_PADDING_WIDTH = Integer.getInteger("contendedPaddingWidth", 128);
+    static final int DEFAULT_FIELD_ALLOCATION_STYLE = Integer.getInteger("fieldAllocationStyle", 1);
+
     private final DataModel model;
     private final boolean takeHierarchyGaps;
     private final boolean takeSuperGaps;
     private final boolean autoAlign;
+    private final boolean compactFields;
+    private final int fieldAllocationStyle;
 
     public HotSpotLayouter(DataModel model) {
-        this(model, false, false, false);
+        this(model, false, false, false, true, DEFAULT_FIELD_ALLOCATION_STYLE);
     }
 
-    public HotSpotLayouter(DataModel model, boolean takeHierarchyGaps, boolean takeSuperGaps, boolean autoAlign) {
+    public HotSpotLayouter(DataModel model, boolean takeHierarchyGaps, boolean takeSuperGaps, boolean autoAlign, boolean compactFields, int fieldAllocationStyle) {
         this.model = model;
         this.takeHierarchyGaps = takeHierarchyGaps;
         this.takeSuperGaps = takeSuperGaps;
         this.autoAlign = autoAlign;
+        this.compactFields = compactFields;
+        this.fieldAllocationStyle = fieldAllocationStyle;
     }
 
     @Override
@@ -69,64 +96,448 @@
             int scale = model.sizeOf(cd.arrayComponentType());
 
             long instanceSize = base + cd.arrayLength() * scale;
-            instanceSize = MathUtil.align(instanceSize, autoAlign ? Math.max(model.objectAlignment(), scale) : model.objectAlignment());
+            instanceSize = MathUtil.align(instanceSize, model.objectAlignment());
             base = MathUtil.align(base, Math.max(4, scale));
 
             result.add(new FieldLayout(FieldData.create(cd.arrayClass(), "<elements>", cd.arrayComponentType()), base, scale * cd.arrayLength()));
             return new ClassLayout(cd, result, model.arrayHeaderSize(), instanceSize, false);
         }
 
-        List<String> hierarchy = cd.classHierarchy();
+        ArrayList<ClassData> classDataClassHierarchy = new ArrayList<ClassData>();
+        ClassData cld = cd;
+        classDataClassHierarchy.add(cld);
 
-        BitSet claimed = new BitSet();
+        while ((cld = cld.superClass()) != null) {
+            classDataClassHierarchy.add(0, cld);
+        }
 
-        claimed.set(0, model.headerSize());
+        int superClassLastOopOffset = 0;
+        int superClassFieldsSize = 0;
+        int nextPaddedOffset = 0;
+        ArrayList<Integer> hierarchyGapsOffsets = new ArrayList<Integer>();
+        ArrayList<Integer> hierarchyGapsSizes = new ArrayList<Integer>();
+        ArrayList<Integer> superGapsOffsets = new ArrayList<Integer>();
+        ArrayList<Integer> superGapsSizes = new ArrayList<Integer>();
 
-        for (String k : hierarchy) {
+        for (ClassData clsData : classDataClassHierarchy) {
+            EnumMap<FieldAllocationType, Integer> fieldsAllocationCount = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
+            EnumMap<FieldAllocationType, Integer> nextOffset = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
+            EnumMap<FieldAllocationType, ArrayDeque<Integer>> spaceOffset = new EnumMap<FieldAllocationType, ArrayDeque<Integer>>(FieldAllocationType.class);
+            EnumMap<FieldAllocationType, Integer> allocationTypeSizes = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
 
-            Collection<FieldData> fields = cd.fieldsFor(k);
+            for (FieldAllocationType atype : FieldAllocationType.values()) {
+                fieldsAllocationCount.put(atype, 0);
+                nextOffset.put(atype,  0);
+                spaceOffset.put(atype, new ArrayDeque<Integer>());
+            }
+            allocationTypeSizes.put(OOP,    model.sizeOf("oop"));
+            allocationTypeSizes.put(BYTE,   model.sizeOf("byte"));
+            allocationTypeSizes.put(SHORT,  model.sizeOf("short"));
+            allocationTypeSizes.put(WORD,   model.sizeOf("int"));
+            allocationTypeSizes.put(DOUBLE, model.sizeOf("long"));
 
-            SortedSet<FieldLayout> current = new TreeSet<FieldLayout>();
-            for (int size : new int[]{8, 4, 2, 1}) {
-                for (FieldData f : fields) {
-                    int fSize = model.sizeOf(f.typeClass());
-                    if (fSize != size) continue;
+            for (FieldData f : clsData.ownFields()) {
+                FieldAllocationType atype = FieldAllocationType.allocationTypeFor(f);
+                Integer count = fieldsAllocationCount.get(atype);
+                fieldsAllocationCount.put(atype, ++count);
+            }
 
-                    for (int t = 0; t < Integer.MAX_VALUE; t++) {
-                        if (claimed.get(t * size, (t + 1) * size).isEmpty()) {
-                            claimed.set(t * size, (t + 1) * size);
-                            current.add(new FieldLayout(f, t * size, size));
-                            break;
+            // Count the contended fields by type.
+            int contendedCount = 0;
+            EnumMap<FieldAllocationType, Integer> facContended = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
+
+            for (FieldData f : clsData.ownFields()) {
+                FieldAllocationType atype = FieldAllocationType.allocationTypeFor(f);
+                if (f.isContended()) {
+                    Integer count = facContended.get(atype);
+                    facContended.put(atype, count == null ? 1 : ++count);
+                    contendedCount++;
-                        }
-                    }
+                }
+            }
+
+            int fieldsStart = (clsData.superClass() == null ? model.headerSize() : 0) + superClassFieldsSize;
+            int nextFieldOffset = fieldsStart;
+
+            boolean isContendedClass = clsData.isContended();
+
+            // Class is contended, pad before all the fields
+            if (isContendedClass) {
+                nextFieldOffset += CONTENDED_PADDING_WIDTH;
-                }
+            }
+
+            // Compute the non-contended fields count.
+            // The packing code below relies on these counts to determine if some field
+            // can be squeezed into the alignment gap. Contended fields are obviously
+            // exempt from that.
+            int doubleCount = fieldsAllocationCount.get(DOUBLE) - (facContended.containsKey(DOUBLE) ? facContended.get(DOUBLE) : 0);
+            int wordCount   = fieldsAllocationCount.get(WORD)   - (facContended.containsKey(WORD)   ? facContended.get(WORD)   : 0);
+            int shortCount  = fieldsAllocationCount.get(SHORT)  - (facContended.containsKey(SHORT)  ? facContended.get(SHORT)  : 0);
+            int byteCount   = fieldsAllocationCount.get(BYTE)   - (facContended.containsKey(BYTE)   ? facContended.get(BYTE)   : 0);
+            int oopCount    = fieldsAllocationCount.get(OOP)    - (facContended.containsKey(OOP)    ? facContended.get(OOP)    : 0);
+
+            int firstOopOffset = 0; // will be set for first oop field
+
+            boolean compactFields = this.compactFields;
+            int allocationStyle = this.fieldAllocationStyle;
+            if (allocationStyle < 0 || allocationStyle > 2) {
+                allocationStyle = 1; // Optimistic
             }
-            result.addAll(current);
 
+            // Use default fields allocation order for classes, which have predefined hard-coded fields offsets.
+            if ((allocationStyle != 0 || compactFields) &&
+                    predefinedFieldsOffsetsClasses.contains(clsData.name())) {
+                allocationStyle = 0;     // Allocate oops first
+                compactFields = false; // Don't compact fields
+            }
+
+            // Rearrange fields for a given allocation style
+            if (allocationStyle == 0) {
+                // Fields order: oops, longs/doubles, ints, shorts/chars, bytes, padded fields
+                nextOffset.put(OOP, nextFieldOffset);
+                nextOffset.put(DOUBLE,nextOffset.get(OOP) + (oopCount * model.sizeOf("oop")));
+            } else if (allocationStyle == 1) {
+                // Fields order: longs/doubles, ints, shorts/chars, bytes, oops, padded fields
+                nextOffset.put(DOUBLE, nextFieldOffset);
+            } else if (allocationStyle == 2) {
+                // Fields allocation: oops fields in super and sub classes are together.
+                if (superClassFieldsSize > 0) {
+                    if (clsData.superClass() != null && clsData.superClass().oopsCount() > 0) {
+                        if (superClassLastOopOffset + model.sizeOf("oop") == nextFieldOffset) {
+                            allocationStyle = 0;   // allocate oops first
+                            nextOffset.put(OOP, nextFieldOffset);
+                            nextOffset.put(DOUBLE, nextOffset.get(OOP) + (oopCount * model.sizeOf("oop")));
+                        }
+                    }
+                }
+                if (allocationStyle == 2) {
+                    allocationStyle = 1;     // allocate oops last
+                    nextOffset.put(DOUBLE, nextFieldOffset);
+                }
+            } else {
+                throw new IllegalStateException();
+            }
+
+            if (takeHierarchyGaps) {
+                Iterator<Integer> itHierarchyGapsOffsets = hierarchyGapsOffsets.iterator();
+                Iterator<Integer> itHierarchyGapsSizes = hierarchyGapsSizes.iterator();
+                int currentGapIndex = 0;
+
+                // Allocate available fields into the hierarchy gaps.
+                while (itHierarchyGapsOffsets.hasNext() && itHierarchyGapsSizes.hasNext()) {
+                    int offset = itHierarchyGapsOffsets.next();
+                    int length = itHierarchyGapsSizes.next();
+
+                    // Superclass rounded up by 4 bytes => there can be maximum 3 bytes gap
+                    if (length >= allocationTypeSizes.get(SHORT) && shortCount > 0) {
+                        int nextShortOffset = MathUtil.align(offset, allocationTypeSizes.get(SHORT));
+                        boolean alignmentGap = offset != nextShortOffset; // Whether there is alignment gap
+
+                        // Insert byte field before the aligned short
+                        if (alignmentGap && byteCount > 0) {
+                            byteCount -= 1;
+                            spaceOffset.get(BYTE).push(offset);
+                            length -= allocationTypeSizes.get(BYTE);
+                            offset += allocationTypeSizes.get(BYTE);
+                            alignmentGap = false;
+                        }
+                        // Insert short field
+                        if (nextShortOffset + allocationTypeSizes.get(SHORT) <= offset + length) {
+                            shortCount -= 1;
+                            spaceOffset.get(SHORT).push(nextShortOffset);
+                            length -= allocationTypeSizes.get(SHORT);
+
+                            // If there is still alignment gap do not increment gap offset
+                            if (!alignmentGap) {
+                                offset += allocationTypeSizes.get(SHORT);
+                            }
+                        }
+                    }
+
+                    // Fill the gap with byte fields
+                    while (length > 0 && byteCount > 0) {
+                        byteCount -= 1;
+                        spaceOffset.get(BYTE).push(offset);
+                        length -= allocationTypeSizes.get(BYTE);
+                        offset += allocationTypeSizes.get(BYTE);
+                    }
+
+                    // The gap is filled, remove it from the lists
+                    if (length == 0) {
+                        itHierarchyGapsOffsets.remove();
+                        itHierarchyGapsSizes.remove();
+                        continue;
+                    } else {
+                        hierarchyGapsOffsets.set(currentGapIndex, offset);
+                        hierarchyGapsSizes.set(currentGapIndex, length);
+                    }
+
+                    currentGapIndex++;
+                }
+            }
+
             if (takeSuperGaps) {
-                // do nothing
-            } else if (takeHierarchyGaps) {
-                // claim only the class body up to the field
-                int lastSet = claimed.length();
-                claimed.set(0, lastSet);
+                Iterator<Integer> itSuperGapsOffsets = superGapsOffsets.iterator();
+                Iterator<Integer> itSuperGapsSizes = superGapsSizes.iterator();
+                int currentGapIndex = 0;
+
+                // Allocate available fields into the super gaps.
+                while (itSuperGapsOffsets.hasNext() && itSuperGapsSizes.hasNext()) {
+                    int offset = itSuperGapsOffsets.next();
+                    int length = itSuperGapsSizes.next();
+
+                    if (length >= allocationTypeSizes.get(WORD) && wordCount > 0) {
+                        int nextWordOffset = MathUtil.align(offset, allocationTypeSizes.get(WORD));
+
+                        if (nextWordOffset + allocationTypeSizes.get(SHORT) <= offset + length) {
+                            wordCount -= 1;
+                            spaceOffset.get(WORD).push(offset);
+                            length -= allocationTypeSizes.get(WORD);
+                            offset += allocationTypeSizes.get(WORD);
+                        }
+                    }
+
+                    if (length >= allocationTypeSizes.get(SHORT) && shortCount > 0) {
+                        int nextShortOffset = MathUtil.align(offset, allocationTypeSizes.get(SHORT));
+                        boolean alignmentGap = offset != nextShortOffset; // Whether there is alignment gap
+
+                        // Insert byte field before the aligned short
+                        if (alignmentGap && byteCount > 0) {
+                            byteCount -= 1;
+                            spaceOffset.get(BYTE).push(offset);
+                            length -= allocationTypeSizes.get(BYTE);
+                            offset += allocationTypeSizes.get(BYTE);
+                            alignmentGap = false;
+                        }
+                        // Insert short field
+                        if (nextShortOffset + allocationTypeSizes.get(SHORT) <= offset + length) {
+                            shortCount -= 1;
+                            spaceOffset.get(SHORT).push(nextShortOffset);
+                            length -= allocationTypeSizes.get(SHORT);
+
+                            // If there is still alignment gap do not increment gap offset
+                            if (!alignmentGap) {
+                                offset += allocationTypeSizes.get(SHORT);
+                            }
+                        }
+                    }
+
+                    while (length > 0 && byteCount > 0) {
+                        byteCount -= 1;
+                        spaceOffset.get(BYTE).push(offset);
+                        length -= allocationTypeSizes.get(BYTE);
+                        offset += allocationTypeSizes.get(BYTE);
+                    }
+                    // Allocate oop field in the gap if there are no other fields for that.
+                    if (length >= allocationTypeSizes.get(OOP) && oopCount > 0 &&
+                            allocationStyle != 0) { // when oop fields not first
+                        oopCount -= 1;
+                        spaceOffset.get(OOP).push(offset);
+                        length -= allocationTypeSizes.get(OOP);
+                        offset += allocationTypeSizes.get(OOP);
+                    }
+
+                    // The gap is filled, remove it from the lists
+                    if (length == 0) {
+                        itSuperGapsOffsets.remove();
+                        itSuperGapsSizes.remove();
-            } else {
+                    } else {
-                // claim the entire class body, plus some alignment
-                int lastSet = claimed.length();
-                claimed.set(0, MathUtil.align(lastSet, model.sizeOf("java.lang.Object")));
+                        superGapsOffsets.set(currentGapIndex, offset);
+                        superGapsSizes.set(currentGapIndex, length);
+                        currentGapIndex++;
-            }
-        }
+                    }
+                }
+            }
 
-        int instanceSize;
-        if (autoAlign) {
-            int a = 4;
-            for (FieldLayout f : result) {
-                a = Math.max(a, model.sizeOf(f.typeClass()));
+            // Try to squeeze some of the fields into the gaps due to
+            // long/double alignment.
+            if (doubleCount > 0) {
+                int offset = nextOffset.get(DOUBLE);
+                nextOffset.put(DOUBLE, MathUtil.align(offset, allocationTypeSizes.get(DOUBLE)));
+                if (offset != nextOffset.get(DOUBLE)) {
+                    int length = nextOffset.get(DOUBLE) - offset;
+
+                    // Allocate available fields into the gap before double field.
+                    if (compactFields) {
+                        if (wordCount > 0) {
+                            wordCount -= 1;
+                            spaceOffset.get(WORD).push(offset);
+                            length -= allocationTypeSizes.get(WORD);
+                            offset += allocationTypeSizes.get(WORD);
-            }
+                        }
-            instanceSize = MathUtil.align(claimed.length(), a);
+                        while (length >= allocationTypeSizes.get(SHORT) && shortCount > 0) {
+                            shortCount -= 1;
+                            spaceOffset.get(SHORT).push(offset);
+                            length -= allocationTypeSizes.get(SHORT);
+                            offset += allocationTypeSizes.get(SHORT);
+                        }
+                        while (length > 0 && byteCount > 0) {
+                            byteCount -= 1;
+                            spaceOffset.get(BYTE).push(offset);
+                            length -= allocationTypeSizes.get(BYTE);
+                            offset += allocationTypeSizes.get(BYTE);
+                        }
+                        // Allocate oop field in the gap if there are no other fields for that.
+                        if (length >= allocationTypeSizes.get(OOP) && oopCount > 0 &&
+                                allocationStyle != 0) { // when oop fields not first
+                            oopCount -= 1;
+                            spaceOffset.get(OOP).push(offset);
+                            length -= allocationTypeSizes.get(OOP);
+                            offset += allocationTypeSizes.get(OOP);
+                        }
+                    }
+
+                    if (takeSuperGaps && length > 0) {
+                        superGapsOffsets.add(offset);
+                        superGapsSizes.add(length);
+                    }
+                }
+            }
+
+            nextOffset.put(WORD,  nextOffset.get(DOUBLE) + (doubleCount * allocationTypeSizes.get(DOUBLE)));
+            nextOffset.put(SHORT, nextOffset.get(WORD)   +   (wordCount * allocationTypeSizes.get(WORD)));
+            nextOffset.put(BYTE,  nextOffset.get(SHORT)  +  (shortCount * allocationTypeSizes.get(SHORT)));
+            nextPaddedOffset = nextOffset.get(BYTE) + byteCount;
+
+            // let oops jump before padding with this allocation style
+            if (allocationStyle == 1) {
+                nextOffset.put(OOP, nextPaddedOffset);
+                if (oopCount > 0) {
+                    nextOffset.put(OOP, MathUtil.align(nextOffset.get(OOP), allocationTypeSizes.get(OOP)));
+                }
+                nextPaddedOffset = nextOffset.get(OOP) + (oopCount * allocationTypeSizes.get(OOP));
+            }
+
+            Set<FieldData> layoutedFields = new HashSet<FieldData>();
+
+            // Iterate over fields again and compute correct offsets.
+            // The field allocation type was temporarily stored in the offset slot.
+            // oop fields are located before non-oop fields.
+            for (FieldData f : clsData.ownFields()) {
+
+                // skip already laid out fields
+                if (layoutedFields.contains(f)) continue;
+
+                // contended instance fields are handled below
+                if (f.isContended()) continue;
+
+                FieldAllocationType atype = FieldAllocationType.allocationTypeFor(f);
+                int allocationTypeSize = allocationTypeSizes.get(atype);
+                Integer allocationTypeSpaceOffset = spaceOffset.get(atype).poll();
+
+                // pack the rest of the fields
+                int realOffset;
+                if (atype == DOUBLE) {
+                    int nextDoubleOffset = nextOffset.get(DOUBLE);
+                    realOffset = nextOffset.get(DOUBLE);
+                    nextOffset.put(atype, nextDoubleOffset + allocationTypeSize);
-        } else {
+                } else {
-            instanceSize = MathUtil.align(claimed.length(), model.objectAlignment());
+                    if (allocationTypeSpaceOffset != null) {
+                        realOffset = allocationTypeSpaceOffset;
+                    } else {
+                        int allocationTypeNextOffset = nextOffset.get(atype);
+                        realOffset = allocationTypeNextOffset;
+                        nextOffset.put(atype, allocationTypeNextOffset + allocationTypeSize);
-        }
+                    }
+                }
 
+                layoutedFields.add(f);
+                result.add(new FieldLayout(f, realOffset, model.sizeOf(f.typeClass())));
+
+                if (atype == OOP) {
+                    superClassLastOopOffset = realOffset;
+                }
+            }
+
+            // Handle the contended cases.
+            //
+            // Each contended field should not intersect the cache line with another contended field.
+            // In the absence of alignment information, we end up with pessimistically separating
+            // the fields with full-width padding.
+            //
+            // Additionally, this should not break alignment for the fields, so we round the alignment up
+            // for each field.
+            if (contendedCount > 0) {
+
+                // if there is at least one contended field, we need to have pre-padding for them
+                nextPaddedOffset += CONTENDED_PADDING_WIDTH;
+
+                // collect all contended groups
+                HashSet<String> contendedGroups = new HashSet<String>();
+
+                for (FieldData f : clsData.ownFields()) {
+                    if (f.isContended()) {
+                        contendedGroups.add(f.contendedGroup());
+                    }
+                }
+
+                for (String currentGroup : contendedGroups) {
+
+                    for (FieldData f : clsData.ownFields()) {
+
+                        // skip already laid out fields
+                        if (layoutedFields.contains(f)) continue;
+
+                        // skip non-contended fields and fields from different group
+                        if (!f.isContended() || !f.contendedGroup().equals(currentGroup)) continue;
+
+                        FieldAllocationType atype = FieldAllocationType.allocationTypeFor(f);
+
+                        int allocationTypeSize = allocationTypeSizes.get(atype);
+                        nextPaddedOffset = MathUtil.align(nextPaddedOffset, allocationTypeSize);
+                        int realOffset = nextPaddedOffset;
+                        nextPaddedOffset += allocationTypeSize;
+
+                        if (atype == OOP && firstOopOffset == 0) { // Undefined
+                            firstOopOffset = realOffset;
+                        }
+
+                        if (f.contendedGroup().equals("")) {
+                            // Contended group defines the equivalence class over the fields:
+                            // the fields within the same contended group are not inter-padded.
+                            // The only exception is default group, which does not incur the
+                            // equivalence, and so requires intra-padding.
+                            nextPaddedOffset += CONTENDED_PADDING_WIDTH;
+                        }
+
+                        result.add(new FieldLayout(f, realOffset, model.sizeOf(f.typeClass())));
+                    }
+
+                    // Start laying out the next group.
+                    // Note that this will effectively pad the last group in the back;
+                    // this is expected to alleviate memory contention effects for
+                    // subclass fields and/or adjacent object.
+                    // If this was the default group, the padding is already in place.
+                    if (!currentGroup.equals("")) {
+                        nextPaddedOffset += CONTENDED_PADDING_WIDTH;
+                    }
+                }
+            }
+
+            // Entire class is contended, pad in the back.
+            // This helps to alleviate memory contention effects for subclass fields
+            // and/or adjacent object.
+            if (isContendedClass) {
+                nextPaddedOffset += CONTENDED_PADDING_WIDTH;
+            }
+
+            superClassFieldsSize = MathUtil.align(nextPaddedOffset, 4);
+
+            if (takeHierarchyGaps && superClassFieldsSize != nextPaddedOffset) {
+                hierarchyGapsOffsets.add(nextPaddedOffset);
+                hierarchyGapsSizes.add(superClassFieldsSize - nextPaddedOffset);
+            }
+        }
+
+        int minAlignment = autoAlign ? 4 : model.objectAlignment();
+        for (String k : cd.classHierarchy()) {
+            Collection<FieldData> fields = cd.fieldsFor(k);
+            for (FieldData f : fields) {
+                minAlignment = Math.max(minAlignment, model.sizeOf(f.typeClass()));
+            }
+        }
+
+        int instanceSize = MathUtil.align(nextPaddedOffset, minAlignment);
+
         return new ClassLayout(cd, result, model.headerSize(), instanceSize, true);
     }
 
@@ -135,6 +546,8 @@
         return "VM Layout Simulation (" + model
                 + (takeHierarchyGaps ? ", hierarchy gaps" : "")
                 + (takeSuperGaps ? ", super gaps" : "")
-                + (autoAlign ? ", autoalign" : "") + ")";
+                + (autoAlign ? ", autoalign" : "")
+                + (compactFields ? ", compact fields" : "")
+                + ", field allocation style: " + fieldAllocationStyle + ")";
     }
 }
Index: jol-core/src/main/java/org/openjdk/jol/layouters/FieldAllocationType.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-core/src/main/java/org/openjdk/jol/layouters/FieldAllocationType.java	(revision 79+:e8e889a8ae3d+)
+++ jol-core/src/main/java/org/openjdk/jol/layouters/FieldAllocationType.java	(revision 79+:e8e889a8ae3d+)
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jol.layouters;
+
+import org.openjdk.jol.info.FieldData;
+
+enum FieldAllocationType {
+    OOP,        // Oops
+    BYTE,       // Boolean, Byte
+    SHORT,      // shorts, char
+    WORD,       // ints
+    DOUBLE,     // aligned long or double
+    ;
+
+    static FieldAllocationType allocationTypeFor(FieldData field) {
+        String simpleName = field.typeClass();
+        if (simpleName.equals("boolean") || simpleName.equals("byte")) {
+            return FieldAllocationType.BYTE;
+        } else if (simpleName.equals("short") || simpleName.equals("char")) {
+            return FieldAllocationType.SHORT;
+        } else if (simpleName.equals("int") || simpleName.equals("float")) {
+            return FieldAllocationType.WORD;
+        } else if (simpleName.equals("long") || simpleName.equals("double")) {
+            return FieldAllocationType.DOUBLE;
+        } else {
+            return FieldAllocationType.OOP;
+        }
+    }
+}
Index: jol-cli/src/main/java/org/openjdk/jol/operations/ObjectIdealPacking.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- jol-cli/src/main/java/org/openjdk/jol/operations/ObjectIdealPacking.java	(revision 79:e8e889a8ae3d64ff2447ed587a0762b5a1e6f951)
+++ jol-cli/src/main/java/org/openjdk/jol/operations/ObjectIdealPacking.java	(revision 79+:e8e889a8ae3d+)
@@ -85,25 +85,28 @@
                         ClassLayout raw = new RawLayouter(model).layout(cd);
                         ClassLayout vm = new CurrentLayouter().layout(cd);
 
-                        ClassLayout hsDefault_m = new HotSpotLayouter(model, false, false, false).layout(cd);
-                        ClassLayout hsHier_m = new HotSpotLayouter(model, true, false, false).layout(cd);
-                        ClassLayout hsSuper_m = new HotSpotLayouter(model, true, true, false).layout(cd);
-
-                        ClassLayout hsDefault_a = new HotSpotLayouter(model, false, false, true).layout(cd);
-                        ClassLayout hsHier_a = new HotSpotLayouter(model, true, false, true).layout(cd);
-                        ClassLayout hsSuper_a = new HotSpotLayouter(model, true, true, true).layout(cd);
-
                         System.out.printf("%3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d, %s%n",
                                 model.headerSize(),
                                 raw.instanceSize(),
-                                vm.instanceSize(),
-                                hsDefault_m.instanceSize(),
-                                hsHier_m.instanceSize(),
-                                hsSuper_m.instanceSize(),
-                                hsDefault_a.instanceSize(),
-                                hsHier_a.instanceSize(),
-                                hsSuper_a.instanceSize(),
-                                klass.getName());
+                                vm.instanceSize());
+
+                        final boolean[] BOOLS = {false, true};
+                        for (boolean hierarchyGaps : BOOLS) {
+                            for (boolean superClassGaps : BOOLS) {
+                                for (boolean autoAlign : BOOLS) {
+                                    for (boolean compactFields : BOOLS) {
+                                        for (int fieldAllocationStyle : new int[]{0, 1, 2}) {
+                                            ClassLayout l = new HotSpotLayouter(model,
+                                                    hierarchyGaps, superClassGaps, autoAlign,
+                                                    compactFields, fieldAllocationStyle).layout(cd);
+                                            System.out.printf("%3d, ", l.instanceSize());
+                                        }
+                                    }
+                                }
+                            }
+                        }
+
+                        System.out.printf("%s%n", klass.getName());
                     } catch (VerifyError t) {
                     } catch (IncompatibleClassChangeError t) {
                     } catch (SecurityException t) {


More information about the jol-dev mailing list