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

Тимур Гибадуллин timur.gbd at gmail.com
Sun Jun 5 21:47:55 UTC 2016


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.

On 24.04.16 20:59, Timur Gibadullin wrote:
> Hi, ok, I'll check out.

-------------- next part --------------
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 78:7bf2496e895b7b0bab77314646ddb94f76ddc8aa)
+++ jol-core/src/main/java/org/openjdk/jol/info/ClassData.java	(revision 78+:7bf2496e895b+)
@@ -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,21 @@
     }
 
     /**
+     * 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;
+    }
+
+    /**
      * Get the fields' data for the given class.
      *
      * @param klass class name
@@ -224,6 +265,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 78:7bf2496e895b7b0bab77314646ddb94f76ddc8aa)
+++ jol-core/src/test/java/org/openjdk/jol/layouters/LayouterInvariantsTest.java	(revision 78+:7bf2496e895b+)
@@ -58,8 +58,10 @@
                 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, 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 78:7bf2496e895b7b0bab77314646ddb94f76ddc8aa)
+++ jol-core/src/main/java/org/openjdk/jol/info/FieldData.java	(revision 78+:7bf2496e895b+)
@@ -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-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,34 +31,96 @@
 import org.openjdk.jol.info.FieldLayout;
 import org.openjdk.jol.util.MathUtil;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.IllegalStateException;
+import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+
 /**
  * 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);
+
     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, true, -1);
     }
 
-    public HotSpotLayouter(DataModel model, boolean takeHierarchyGaps, boolean takeSuperGaps, boolean autoAlign) {
+    public HotSpotLayouter(DataModel model, boolean takeHierarchyGaps, boolean takeSuperGaps, boolean compactFields, int fieldAllocationStyle) {
         this.model = model;
         this.takeHierarchyGaps = takeHierarchyGaps;
         this.takeSuperGaps = takeSuperGaps;
-        this.autoAlign = autoAlign;
+        this.compactFields = compactFields;
+        this.fieldAllocationStyle = fieldAllocationStyle;
     }
 
+    private enum FieldAllocationType {
+        OOP,        // Oops
+        BYTE,       // Boolean, Byte
+        SHORT,      // shorts, char
+        WORD,       // ints
+        DOUBLE,     // aligned long or double
+        BAD_ALLOCATION_TYPE
+    }
+
+    private FieldAllocationType allocationTypeFor(Field field) {
+        String simpleName = field.getType().getSimpleName();
+        Class superClass = field.getType();
+
+        while ((superClass = superClass.getSuperclass()) != null) {
+            simpleName = superClass.getSimpleName();
+        }
+
+        if (simpleName.equals("Object")) {
+            return FieldAllocationType.OOP;
+        } else 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;
+        }
+
+        return FieldAllocationType.BAD_ALLOCATION_TYPE;
+    }
+
     @Override
     public ClassLayout layout(ClassData cd) {
         SortedSet<FieldLayout> result = new TreeSet<FieldLayout>();
@@ -69,64 +131,318 @@
             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);
         }
 
+        EnumMap<FieldAllocationType, Integer> fieldsAllocationCount = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
+        EnumMap<FieldAllocationType, Integer> nextOffset = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
+        EnumMap<FieldAllocationType, Integer> spaceCount = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
+        EnumMap<FieldAllocationType, Integer> spaceOffset = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
+        EnumMap<FieldAllocationType, Integer> allocationTypeSizes = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
+
+        for (FieldAllocationType atype : FieldAllocationType.values()) {
+            fieldsAllocationCount.put(atype, 0);
+            nextOffset.put(atype, 0);
+            spaceCount.put(atype, 0);
+            spaceOffset.put(atype, 0);
+        }
+        allocationTypeSizes.put(FieldAllocationType.OOP, model.sizeOf("oop"));
+        allocationTypeSizes.put(FieldAllocationType.BYTE, model.sizeOf("byte"));
+        allocationTypeSizes.put(FieldAllocationType.SHORT, model.sizeOf("short"));
+        allocationTypeSizes.put(FieldAllocationType.WORD, model.sizeOf("int"));
+        allocationTypeSizes.put(FieldAllocationType.DOUBLE, model.sizeOf("long"));
+
         List<String> hierarchy = cd.classHierarchy();
+        for (String k : hierarchy) {
 
-        BitSet claimed = new BitSet();
+            Collection<FieldData> fields = cd.fieldsFor(k);
+            for (FieldData f : fields) {
+                FieldAllocationType atype = allocationTypeFor(f.refField());
+                Integer count = (Integer) fieldsAllocationCount.get(atype);
+                fieldsAllocationCount.put(atype, ++count);
+            }
+        }
 
-        claimed.set(0, model.headerSize());
+        int fieldSize = cd.superClass() == null ? 0 : cd.superClass().fieldSize(model);
+        int firstOopOffset = 0;
+        int nextFieldOffset = 0;
+        int nextPaddedOffset = 0;
 
+        // Count the contended fields by type.
+        int contendedCount = 0;
+        EnumMap<FieldAllocationType, Integer> facContended = new EnumMap<FieldAllocationType, Integer>(FieldAllocationType.class);
+
         for (String k : hierarchy) {
 
             Collection<FieldData> fields = cd.fieldsFor(k);
-
-            SortedSet<FieldLayout> current = new TreeSet<FieldLayout>();
-            for (int size : new int[]{8, 4, 2, 1}) {
-                for (FieldData f : fields) {
+            for (FieldData f : fields) {
-                    int fSize = model.sizeOf(f.typeClass());
-                    if (fSize != size) continue;
+                FieldAllocationType atype = allocationTypeFor(f.refField());
+                if (f.isContended()) {
+                    Integer count = facContended.get(atype);
 
-                    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;
+                    facContended.put(atype, count == null ? 1 : ++count);
+                    contendedCount++;
-                        }
-                    }
-                }
+                }
+            }
+        }
+
+        int fieldsStart = model.headerSize() + fieldSize * model.sizeOf("oop");
+
+        nextFieldOffset = fieldsStart;
+
+        boolean isContendedClass = cd.isContended();
+
+        // Class is contended, pad before all the fields
+        if (isContendedClass) {
+            nextFieldOffset += CONTENDED_PADDING_WIDTH;
-            }
+        }
-            result.addAll(current);
 
-            if (takeSuperGaps) {
-                // do nothing
-            } else if (takeHierarchyGaps) {
-                // claim only the class body up to the field
-                int lastSet = claimed.length();
-                claimed.set(0, lastSet);
+        // 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(FieldAllocationType.DOUBLE) -
+                (facContended.containsKey(FieldAllocationType.DOUBLE) ? facContended.get(FieldAllocationType.DOUBLE) : 0);
+        int wordCount = fieldsAllocationCount.get(FieldAllocationType.WORD) -
+                (facContended.containsKey(FieldAllocationType.WORD) ? facContended.get(FieldAllocationType.WORD) : 0);
+        int shortCount = fieldsAllocationCount.get(FieldAllocationType.SHORT) -
+                (facContended.containsKey(FieldAllocationType.SHORT) ? facContended.get(FieldAllocationType.SHORT) : 0);
+        int byteCount = fieldsAllocationCount.get(FieldAllocationType.BYTE) -
+                (facContended.containsKey(FieldAllocationType.BYTE) ? facContended.get(FieldAllocationType.BYTE) : 0);
+        int oopCount = fieldsAllocationCount.get(FieldAllocationType.OOP) -
+                (facContended.containsKey(FieldAllocationType.OOP) ? facContended.get(FieldAllocationType.OOP) : 0);
+
+        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
+        }
+
+        // Use default fields allocation order for classes, which have predefined hard-coded fields offsets.
+        if ((allocationStyle != 0 || compactFields) &&
+                predefinedFieldsOffsetsClasses.contains(cd.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(FieldAllocationType.OOP, nextFieldOffset);
+            nextOffset.put(FieldAllocationType.DOUBLE,
+                nextOffset.get(FieldAllocationType.OOP) + (oopCount * model.sizeOf("oop")));
+        } else if (allocationStyle == 1) {
+            // Fields order: longs/doubles, ints, shorts/chars, bytes, oops, padded fields
+            nextOffset.put(FieldAllocationType.DOUBLE, nextFieldOffset);
+        } else if (allocationStyle == 2) {
+            // Fields allocation: oops fields in super and sub classes are together.
+            if (allocationStyle == 2) {
+                allocationStyle = 1;     // allocate oops last
+                nextOffset.put(FieldAllocationType.DOUBLE, nextFieldOffset);
+            }
-            } 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")));
+            throw new IllegalStateException();
-            }
+        }
+
+        // Try to squeeze some of the fields into the gaps due to
+        // long/double alignment.
+        if (doubleCount > 0) {
+            int offset = nextOffset.get(FieldAllocationType.DOUBLE);
+            nextOffset.put(FieldAllocationType.DOUBLE, MathUtil.align(offset, allocationTypeSizes.get(FieldAllocationType.DOUBLE)));
+            if (compactFields && offset != nextOffset.get(FieldAllocationType.DOUBLE)) {
+                // Allocate available fields into the gap before double field.
+                int length = nextOffset.get(FieldAllocationType.DOUBLE) - offset;
+                spaceOffset.put(FieldAllocationType.WORD, offset);
+                if (wordCount > 0) {
+                    wordCount -= 1;
+                    spaceCount.put(FieldAllocationType.WORD, 1); // Only one will fit
+                    length -= allocationTypeSizes.get(FieldAllocationType.WORD);
+                    offset += allocationTypeSizes.get(FieldAllocationType.WORD);
-        }
+                }
+                spaceOffset.put(FieldAllocationType.SHORT, offset);
+                while (length >= allocationTypeSizes.get(FieldAllocationType.SHORT) && shortCount > 0) {
+                    shortCount -= 1;
+                    spaceCount.put(FieldAllocationType.SHORT, spaceCount.get(FieldAllocationType.SHORT) + 1);
+                    length -= allocationTypeSizes.get(FieldAllocationType.SHORT);
+                    offset += allocationTypeSizes.get(FieldAllocationType.SHORT);
+                }
+                spaceOffset.put(FieldAllocationType.BYTE, offset);
+                while (length > 0 && byteCount > 0) {
+                    byteCount -= 1;
+                    spaceCount.put(FieldAllocationType.BYTE, spaceCount.get(FieldAllocationType.BYTE) + 1);
+                    length -= 1;
+                }
+                // Allocate oop field in the gap if there are no other fields for that.
+                spaceOffset.put(FieldAllocationType.OOP, offset);
+                if (length >= allocationTypeSizes.get(FieldAllocationType.OOP) && oopCount > 0 &&
+                        allocationStyle != 0) { // when oop fields not first
+                    oopCount -= 1;
+                    spaceCount.put(FieldAllocationType.OOP, 1); // Only one will fit
+                    length -= allocationTypeSizes.get(FieldAllocationType.OOP);
+                    offset += allocationTypeSizes.get(FieldAllocationType.OOP);
+                }
+            }
+        }
 
-        int instanceSize;
-        if (autoAlign) {
-            int a = 4;
-            for (FieldLayout f : result) {
-                a = Math.max(a, model.sizeOf(f.typeClass()));
+        nextOffset.put(FieldAllocationType.WORD, nextOffset.get(FieldAllocationType.DOUBLE) +
+                (doubleCount * allocationTypeSizes.get(FieldAllocationType.DOUBLE)));
+        nextOffset.put(FieldAllocationType.SHORT, nextOffset.get(FieldAllocationType.WORD) +
+                (wordCount * allocationTypeSizes.get(FieldAllocationType.WORD)));
+        nextOffset.put(FieldAllocationType.BYTE, nextOffset.get(FieldAllocationType.SHORT) +
+                (shortCount * allocationTypeSizes.get(FieldAllocationType.SHORT)));
+        nextPaddedOffset = nextOffset.get(FieldAllocationType.BYTE) + byteCount;
+
+        // let oops jump before padding with this allocation style
+        if (allocationStyle == 1) {
+            nextOffset.put(FieldAllocationType.OOP, nextPaddedOffset);
+            if (oopCount > 0) {
+                nextOffset.put(FieldAllocationType.OOP, MathUtil.align(nextOffset.get(FieldAllocationType.OOP), allocationTypeSizes.get(FieldAllocationType.OOP)));
             }
-            instanceSize = MathUtil.align(claimed.length(), a);
+            nextPaddedOffset = nextOffset.get(FieldAllocationType.OOP) + (oopCount * allocationTypeSizes.get(FieldAllocationType.OOP));
+        }
+
+        HashSet<String> layoutedFields = new HashSet<String>();
+
+        // 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 (String k : hierarchy) {
+
+            Collection<FieldData> fields = cd.fieldsFor(k);
+
+            for (FieldData f : fields) {
+
+                // skip already laid out fields
+                if (layoutedFields.contains(f.toString())) continue;
+
+                // contended instance fields are handled below
+                if (f.isContended()) continue;
+
+                int realOffset = 0;
+                FieldAllocationType atype = allocationTypeFor(f.refField());
+                int allocationTypeSize = allocationTypeSizes.get(atype);
+                int allocationTypeSpaceCount = spaceCount.get(atype);
+                int allocationTypeSpaceOffset = spaceOffset.get(atype);
+
+                // pack the rest of the fields
+                if (atype == FieldAllocationType.DOUBLE) {
+                    int nextDoubleOffset = nextOffset.get(atype);
+                    realOffset = nextOffset.get(FieldAllocationType.DOUBLE);
+                    nextOffset.put(atype, nextDoubleOffset + allocationTypeSize);
+                } else if (atype != FieldAllocationType.BAD_ALLOCATION_TYPE) {
+                    if (allocationTypeSpaceCount > 0) {
+                        realOffset = allocationTypeSpaceOffset;
+                        spaceOffset.put(atype, allocationTypeSpaceOffset + allocationTypeSize);
+                        spaceCount.put(atype, allocationTypeSpaceCount - 1);
-        } else {
+                    } else {
-            instanceSize = MathUtil.align(claimed.length(), model.objectAlignment());
+                        int allocationTypeNextOffset = nextOffset.get(atype);
+                        realOffset = allocationTypeNextOffset;
+                        nextOffset.put(atype, allocationTypeNextOffset + allocationTypeSize);
-        }
+                    }
+                } else {
+                    throw new IllegalStateException();
+                }
 
+                layoutedFields.add(f.toString());
+                result.add(new FieldLayout(f, realOffset, model.sizeOf(f.typeClass())));
+            }
+        }
+
+        // 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 (String k : hierarchy) {
+
+                Collection<FieldData> fields = cd.fieldsFor(k);
+
+                for (FieldData f : fields) {
+                    if (f.isContended()) {
+                        contendedGroups.add(f.contendedGroup());
+                    }
+                }
+            }
+
+            for (String currentGroup : contendedGroups) {
+
+                for (String k : hierarchy) {
+
+                    Collection<FieldData> fields = cd.fieldsFor(k);
+
+                    for (FieldData f : fields) {
+
+                        // skip already laid out fields
+                        if (layoutedFields.contains(f.toString())) continue;
+
+                        // skip non-contended fields and fields from different group
+                        if (!f.isContended() || !f.contendedGroup().equals(currentGroup)) continue;
+
+                        int realOffset = 0;
+                        FieldAllocationType atype = allocationTypeFor(f.refField());
+
+                        if (atype != FieldAllocationType.BAD_ALLOCATION_TYPE) {
+                            int allocationTypeSize = allocationTypeSizes.get(atype);
+                            nextPaddedOffset = MathUtil.align(nextPaddedOffset, allocationTypeSize);
+                            realOffset = nextPaddedOffset;
+                            nextPaddedOffset += allocationTypeSize;
+
+                            if (atype == FieldAllocationType.OOP && firstOopOffset == 0) { // Undefined
+                                firstOopOffset = realOffset;
+                            }
+                        } else {
+                            throw new IllegalStateException();
+                        }
+
+                        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 != "") {
+                    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;
+        }
+
+        int notalignedFieldsEnd = nextPaddedOffset;
+        int instanceSize = MathUtil.align(notalignedFieldsEnd, model.objectAlignment());
+
         return new ClassLayout(cd, result, model.headerSize(), instanceSize, true);
     }
 
@@ -135,6 +451,7 @@
         return "VM Layout Simulation (" + model
                 + (takeHierarchyGaps ? ", hierarchy gaps" : "")
                 + (takeSuperGaps ? ", super gaps" : "")
-                + (autoAlign ? ", autoalign" : "") + ")";
+                + (compactFields ? ", compact fields" : "")
+                + "field allocation style: " + fieldAllocationStyle + ")";
     }
 }


More information about the jol-dev mailing list