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