[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