[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