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

Тимур Гибадуллин timur.gbd at gmail.com
Mon Jan 25 23:40:08 UTC 2016


Sent via thunderbird, seems it is now text/plain.

On 25.01.16 19:57, Aleksey Shipilev wrote:
> Nope, the attachment is still striped.
>
> -Aleksey
>
> On 01/25/2016 12:24 PM, Тимур Гибадуллин wrote:
>> Ok, re-sent patch via apple’s mail client.
>>
>>
>>
>>
>>> 22 янв. 2016 г., в 13:21, Aleksey Shipilev <aleksey.shipilev at oracle.com> написал(а):
>>>
>>> Hi Timur,
>>>
>>> The list is stripping the unknown attachments. The attachment should be
>>> one of the following MIME types:
>>> text/plain
>>> text/x-diff
>>> text/x-patch
>>>
>>> You can re-send directly to me if you want to know what's the MIME type
>>> for your current attachment.
>>>
>>> Cheers,
>>> -Aleksey
>>>
>>> On 01/21/2016 03:01 AM, Timur Gibadullin wrote:
>>>> Sorry, forgot to attach the patch.
>>>>
>>>> 2016-01-21 2:44 GMT+03:00 Timur Gibadullin <timur.gbd at gmail.com>:
>>>>
>>>>> Hi! Thanks for the jol, which is such a handy tool. I'd like to improve it
>>>>> and propose patch. I am trying to solve a bug with hotspot layouter, which
>>>>> is described in the issue
>>>>> <https://bugs.openjdk.java.net/browse/CODETOOLS-7901019> in the bug
>>>>> database. And this is my first attempt to solve it, I prepared a patch (sending
>>>>> as attahced to this mail). In the patch I followed Aleksey's comment from
>>>>> the issue and added fieldAllocationStyle option to the HotspotLayouter and changed
>>>>> HotspotLayouter's layout, now it emulates layout_fields method of the ClassFileParser
>>>>> from Hotspot, actually, I ported this method from the Hotspot, refactored
>>>>> it and dropped some unnecessary for the jol parts. Also I made little
>>>>> additions to FieldData and ClassData classes, they are required by the new layout
>>>>> method, and added a new example, which is called JOLSample_26_Hotspot.
>>>>> Please, share your thoughts and give me any feedback and comments about my
>>>>> patch.
>>>>>
>>>>>
>>>>> --
>>>>> Best regards, Timur Gibadullin
>>>>>
>>>>
>>>>
>>>
>

-------------- next part --------------
# HG changeset patch
# User Timur Gibadullin <timur.gbd at gmail.com>
# Date 1453332792 -10800
#      Thu Jan 21 02:33:12 2016 +0300
# Node ID 72b69400980fe84c159e35f3ff053c68d6925297
# Parent  4443d2696dcf87184e805dd475774cffee8c3cc1
7901019: jol estimates do not take AllocationStyle into the account

diff -r 4443d2696dcf -r 72b69400980f jol-core/src/main/java/org/openjdk/jol/info/ClassData.java
--- a/jol-core/src/main/java/org/openjdk/jol/info/ClassData.java	Wed Jan 13 17:09:03 2016 +0300
+++ b/jol-core/src/main/java/org/openjdk/jol/info/ClassData.java	Thu Jan 21 02:33:12 2016 +0300
@@ -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,6 +96,13 @@
         }
 
         ClassData cd = new ClassData(o, klass.getCanonicalName());
+        Class superKlass = klass.getSuperclass();
+
+        cd.isContended = klass.getAnnotation(Contended.class) != null;
+
+        if (superKlass != null) {
+            cd.addSuperClassData(klass.getSuperclass());
+        }
 
         do {
             for (Field f : klass.getDeclaredFields()) {
@@ -113,6 +124,8 @@
     private final String arrayComponentKlass;
     private final int 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
@@ -227,6 +268,24 @@
     }
 
     /**
+     * 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;
+    }
+
+    /**
      * Answer the array class for this class data.
      *
      * @return array class name, e.g. "int[]".
diff -r 4443d2696dcf -r 72b69400980f jol-core/src/main/java/org/openjdk/jol/info/FieldData.java
--- a/jol-core/src/main/java/org/openjdk/jol/info/FieldData.java	Wed Jan 13 17:09:03 2016 +0300
+++ b/jol-core/src/main/java/org/openjdk/jol/info/FieldData.java	Thu Jan 21 02:33:12 2016 +0300
@@ -28,6 +28,7 @@
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+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, computeOffset(field), field.getDeclaringClass().getSimpleName(), field.getName(), field.getType().getSimpleName());
+        Contended contentded = field.getAnnotation(Contended.class);
+        boolean isContended = contentded != null;
+        return new FieldData(
+            field,
+            computeOffset(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 int vmOffset;
+    private final boolean isContended;
+    private final String contendedGroup;
 
-    private FieldData(Field refField, int vmOffset, String hostKlass, String fieldName, String fieldType) {
+    private FieldData(Field refField, int 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;
     }
 
     private static int computeOffset(Field field) {
@@ -108,6 +123,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.
      *
@@ -147,4 +189,7 @@
         }
     }
 
+    public String toString() {
+        return refField.toString();
+    }
 }
diff -r 4443d2696dcf -r 72b69400980f jol-core/src/main/java/org/openjdk/jol/layouters/HotSpotLayouter.java
--- a/jol-core/src/main/java/org/openjdk/jol/layouters/HotSpotLayouter.java	Wed Jan 13 17:09:03 2016 +0300
+++ b/jol-core/src/main/java/org/openjdk/jol/layouters/HotSpotLayouter.java	Thu Jan 21 02:33:12 2016 +0300
@@ -31,32 +31,91 @@
 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, false, true, -1);
     }
 
-    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;
+    }
+
+    private enum FieldAllocationType {
+        OOP,        // Oops
+        BYTE,       // Boolean, Byte, char
+        SHORT,      // shorts
+        WORD,       // ints
+        DOUBLE,     // aligned long or double
+        BAD_ALLOCATION_TYPE
+    }
+
+    private FieldAllocationType allocationTypeFor(Field field) {
+        String simpleName = field.getType().getSimpleName();
+
+        if (simpleName.equals("oop") || simpleName.endsWith("[]")) {
+            return FieldAllocationType.OOP;
+        } else if (simpleName.equals("bool") || simpleName.equals("byte") || simpleName.equals("char")) {
+            return FieldAllocationType.BYTE;
+        } else if (simpleName.equals("short")) {
+            return FieldAllocationType.SHORT;
+        } else if (simpleName.equals("int")) {
+            return FieldAllocationType.WORD;
+        } else if (simpleName.equals("long") || simpleName.equals("double")) {
+            return FieldAllocationType.DOUBLE;
+        }
+
+        return FieldAllocationType.BAD_ALLOCATION_TYPE;
     }
 
     @Override
@@ -77,55 +136,321 @@
             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);
+            for (FieldData f : fields) {
+                FieldAllocationType atype = allocationTypeFor(f.refField());
+                if (f.isContended()) {
+                    Integer count = facContended.get(atype);
 
-            SortedSet<FieldLayout> current = new TreeSet<FieldLayout>();
-            for (int size : new int[]{8, 4, 2, 1}) {
+                    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;
+        }
+
+        // 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 {
+            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, model.sizeOf("long")));
+            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);
+                }
+            }
+        }
+
+        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)));
+            }
+            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 {
+                        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) {
-                    int fSize = model.sizeOf(f.typeClass());
-                    if (fSize != size) continue;
-
-                    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;
-                        }
+                    if (f.isContended()) {
+                        contendedGroups.add(f.contendedGroup());
                     }
                 }
             }
-            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);
-            } else {
-                // claim the entire class body, plus some alignment
-                int lastSet = claimed.length();
-                claimed.set(0, MathUtil.align(lastSet, model.sizeOf("java.lang.Object")));
+            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 instanceEnd = MathUtil.align(notalignedFieldsEnd, 4);
+
         int instanceSize;
         if (autoAlign) {
             int a = 4;
             for (FieldLayout f : result) {
                 a = Math.max(a, model.sizeOf(f.typeClass()));
             }
-            instanceSize = MathUtil.align(claimed.length(), a);
+            instanceSize = MathUtil.align(instanceEnd / 4, a);
         } else {
-            instanceSize = MathUtil.align(claimed.length(), model.objectAlignment());
+            instanceSize = MathUtil.align(instanceEnd, model.objectAlignment());
         }
 
         return new ClassLayout(cd, result, model.headerSize(), instanceSize, true);
diff -r 4443d2696dcf -r 72b69400980f jol-samples/src/main/java/org/openjdk/jol/samples/JOLSample_26_Hotspot.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jol-samples/src/main/java/org/openjdk/jol/samples/JOLSample_26_Hotspot.java	Thu Jan 21 02:33:12 2016 +0300
@@ -0,0 +1,72 @@
+/*
+ * 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.util.VMSupport;
+
+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.Layouter;
+import sun.misc.Contended;
+
+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;
+    }
+}


More information about the jol-dev mailing list