[PATCH] 7901019: jol estimates do not take AllocationStyle into the account
Тимур Гибадуллин
timur.gbd at gmail.com
Wed Jul 13 21:46:43 UTC 2016
Just realized, that the SuperGaps strategy is extension of the
HierarchyGaps strategy, in the previous patch the takeSuperGaps could
not handle gaps, that could be handled by takeHierarchyGaps option.
Fixed this in the new patch.
On 04.07.16 0:43, Тимур Гибадуллин wrote:
> Found a bug in the HotspotLayouter' takeHierarchyGaps and
> takeSuperGaps options while layouting in X64 model (w/o coops), tests
> were ok, because they were run only on current data model, which in my
> laptop is X64 w/ coops. So, in this patch I improved the previous
> {hierarchy/super}gaps filling algorithm and enabled all models in the
> LayouterInvariantsTest. Currently a code for filling
> {hierarchy/super}gaps almost the same and they can be merged, but I
> didn't think up an elegant way to do that w/o creating function or
> writing some weird stuff, would be cool if someone will suggest how to
> refactor this code.
>
> On 03.07.16 16:24, Тимур Гибадуллин wrote:
>> 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+)
@@ -3,7 +3,7 @@
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.*;
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.ClassGenerator;
@@ -12,7 +12,12 @@
public class LayouterInvariantsTest {
private static final boolean[] BOOLS = {false, true};
- private static final DataModel[] MODELS = {new CurrentDataModel()};
+ private static final DataModel[] MODELS = {
+ new CurrentDataModel(),
+ new X86_32_DataModel(),
+ new X86_64_COOPS_DataModel(),
+ new X86_64_DataModel()
+ };
private static final int ITERATIONS = 20000;
@Test
@@ -20,7 +25,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 +42,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 +57,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+)
@@ -31,6 +31,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import sun.misc.Contended;
/**
* Holds the class data, without the layout information.
@@ -92,7 +93,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 +121,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 +140,8 @@
this.arrayKlass = null;
this.arrayComponentKlass = null;
this.isArray = false;
+ this.superClass = null;
+ this.isContended = false;
}
/**
@@ -152,6 +164,8 @@
this.classNames = null;
this.length = length;
this.isArray = true;
+ this.superClass = null;
+ this.isContended = false;
}
/**
@@ -164,6 +178,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 +206,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 +284,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,415 @@
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> 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);
- if (takeSuperGaps) {
- // do nothing
- } else if (takeHierarchyGaps) {
- // claim only the class body up to the field
- int lastSet = claimed.length();
- claimed.set(0, lastSet);
+ // 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 {
- // 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();
}
+
+ if (takeHierarchyGaps || takeSuperGaps) {
+ ListIterator<Integer> itSuperGapsOffsets = superGapsOffsets.listIterator();
+ ListIterator<Integer> itSuperGapsSizes = superGapsSizes.listIterator();
+ int currentGapIndex = 0;
+
+ // Allocate available fields into the hierarchy gaps.
+ while (itSuperGapsOffsets.hasNext() && itSuperGapsSizes.hasNext()) {
+ int offset = itSuperGapsOffsets.next();
+ int length = itSuperGapsSizes.next();
+
+ if (length >= allocationTypeSizes.get(WORD) && wordCount > 0) {
+ int alignedWordOffset = MathUtil.align(offset, allocationTypeSizes.get(WORD));
+
+ // If there is enough space for word after alignment
+ if (alignedWordOffset + allocationTypeSizes.get(WORD) <= offset + length) {
+ wordCount -= 1;
+ spaceOffset.get(WORD).push(alignedWordOffset);
+ length -= allocationTypeSizes.get(WORD);
+
+ // Try to squeeze some of the fields into the gaps due to word alignment
+ if (length >= allocationTypeSizes.get(SHORT) && shortCount > 0) {
+ int alignedShortOffset = MathUtil.align(offset, allocationTypeSizes.get(SHORT));
+
+ // If there is enough space for short after alignment
+ if (alignedShortOffset + allocationTypeSizes.get(SHORT) <= alignedWordOffset) {
+ shortCount -= 1;
+ spaceOffset.get(SHORT).push(alignedShortOffset);
+ length -= allocationTypeSizes.get(SHORT);
+
+ // Try to squeeze some of the fields into the gaps due to short alignment
+ if (length >= allocationTypeSizes.get(BYTE) && byteCount > 0) {
+ byteCount -= 1;
+ spaceOffset.get(BYTE).push(offset);
+ length -= allocationTypeSizes.get(BYTE);
- }
+ }
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+ } else {
+ throw new IllegalStateException();
+ }
+ } else {
+ while (length >= allocationTypeSizes.get(SHORT) && shortCount > 0) {
+ int alignedShortOffset = MathUtil.align(offset, allocationTypeSizes.get(SHORT));
+ int shortAlignmentGapSize = alignedShortOffset - offset;
+ int shortAlignmentGapOffset = offset;
- int instanceSize;
- if (autoAlign) {
- int a = 4;
- for (FieldLayout f : result) {
- a = Math.max(a, model.sizeOf(f.typeClass()));
+ // If there is enough space for short after alignment
+ if (alignedShortOffset + allocationTypeSizes.get(SHORT) <= offset + length) {
+ shortCount -= 1;
+ spaceOffset.get(SHORT).push(alignedShortOffset);
+ length -= allocationTypeSizes.get(SHORT);
+ offset = alignedShortOffset + allocationTypeSizes.get(SHORT);
+
+ if (shortAlignmentGapSize != 0) {
+ if (byteCount > 0) {
+ byteCount -= 1;
+ spaceOffset.get(BYTE).push(shortAlignmentGapOffset);
+ } else {
+ itSuperGapsOffsets.previous();
+ itSuperGapsSizes.previous();
+ itSuperGapsOffsets.add(shortAlignmentGapOffset);
+ itSuperGapsSizes.add(shortAlignmentGapSize);
+ currentGapIndex++;
+ itSuperGapsOffsets.next();
+ itSuperGapsSizes.next();
- }
+ }
- instanceSize = MathUtil.align(claimed.length(), a);
+ length -= allocationTypeSizes.get(BYTE);
+ }
+ }
+ }
+ }
+
+ // 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);
+ }
+
+ // If the gap is filled, remove it from the lists
+ if (length == 0) {
+ itSuperGapsOffsets.remove();
+ itSuperGapsSizes.remove();
+ continue;
- } else {
+ } else {
- instanceSize = MathUtil.align(claimed.length(), model.objectAlignment());
+
+ superGapsOffsets.set(currentGapIndex, offset);
+ superGapsSizes.set(currentGapIndex, length);
- }
+ }
+ currentGapIndex++;
+ }
+ }
+
+ // 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);
+ }
+ 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);
+ }
+ }
+
+ // Add a gap, that is left to super gaps list.
+ // takeSuperGaps strategy extends takeHierarchyGaps, and this is the "extension point".
+ 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 {
+ 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, model.sizeOf("oop"));
+
+ // If there is a gap after object alignment add it to the super gaps list.
+ if ((takeHierarchyGaps || takeSuperGaps) && superClassFieldsSize != nextPaddedOffset) {
+ superGapsOffsets.add(nextPaddedOffset);
+ superGapsSizes.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 +513,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-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) {
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;
+ }
+ }
+}
More information about the jol-dev
mailing list