Last active
August 29, 2015 14:11
-
-
Save raphw/7345bae62abd424fb040 to your computer and use it in GitHub Desktop.
Modified version of JOL's class layout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2012, 2013, 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.info; | |
import org.openjdk.jol.layouters.CurrentLayouter; | |
import org.openjdk.jol.layouters.Layouter; | |
import org.openjdk.jol.util.VMSupport; | |
import java.io.PrintWriter; | |
import java.io.StringWriter; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.SortedSet; | |
/** | |
* Handles the class data *with* the layout information. | |
*/ | |
public class ClassLayout { | |
/** | |
* Produce the class layout for the given class. | |
* This produces the layout for the current VM. | |
* | |
* @param klass class to work on | |
* @return class layout | |
*/ | |
public static ClassLayout parseClass(Class<?> klass) { | |
return parseClass(klass, new CurrentLayouter()); | |
} | |
/** | |
* Produce the class layout for the given class, and given layouter. | |
* | |
* @param klass class to work on | |
* @param layouter class layouter | |
* @return class layout | |
*/ | |
public static ClassLayout parseClass(Class<?> klass, Layouter layouter) { | |
return layouter.layout(ClassData.parseClass(klass)); | |
} | |
private final ClassData classData; | |
private final SortedSet<FieldLayout> fields; | |
private final int headerSize; | |
private final int size; | |
private final boolean detailedHeader; | |
/** | |
* Builds the class layout. | |
* | |
* @param classData class data | |
* @param fields field layouts | |
* @param headerSize header size | |
* @param instanceSize instance size | |
* @param check whether to check important invariants | |
*/ | |
public ClassLayout(ClassData classData, SortedSet<FieldLayout> fields, int headerSize, int instanceSize, boolean check) { | |
this.classData = classData; | |
this.fields = fields; | |
this.headerSize = headerSize; | |
this.size = instanceSize; | |
detailedHeader = false; | |
if (check) { | |
checkInvariants(); | |
} | |
} | |
private ClassLayout(ClassData classData, SortedSet<FieldLayout> fields, int headerSize, int instanceSize, boolean check, boolean detailedHeader) { | |
this.classData = classData; | |
this.fields = fields; | |
this.headerSize = headerSize; | |
this.size = instanceSize; | |
this.detailedHeader = detailedHeader; | |
if (check) { | |
checkInvariants(); | |
} | |
} | |
private void checkInvariants() { | |
long lastOffset = 0; | |
for (FieldLayout f : fields) { | |
if (f.offset() % f.size() != 0) { | |
throw new IllegalStateException("Field " + f + " is not aligned"); | |
} | |
if (f.offset() + f.size() > instanceSize()) { | |
throw new IllegalStateException("Field " + f + " is overflowing the object of size " + instanceSize()); | |
} | |
if (f.offset() < lastOffset) { | |
throw new IllegalStateException("Field " + f + " overlaps with the previous field"); | |
} | |
lastOffset = f.offset() + f.size(); | |
} | |
} | |
/** | |
* Answer the set of fields, including those in superclasses | |
* | |
* @return sorted set of fields | |
*/ | |
public SortedSet<FieldLayout> fields() { | |
return fields; | |
} | |
/** | |
* Answer instance size | |
* | |
* @return instance size | |
*/ | |
public int instanceSize() { | |
return size; | |
} | |
/** | |
* Answer header size | |
* | |
* @return header size | |
*/ | |
public int headerSize() { | |
return headerSize; | |
} | |
@Override | |
public String toString() { | |
StringBuilder sb = new StringBuilder(); | |
for (FieldLayout f : fields()) { | |
sb.append(f).append("\n"); | |
} | |
sb.append("size = ").append(size).append("\n"); | |
return sb.toString(); | |
} | |
/** | |
* Produce printable stringly representation of class layout. | |
* This method does not require alive instance, just the class. | |
* | |
* @return human-readable layout info | |
*/ | |
public String toPrintable() { | |
return toPrintable(null); | |
} | |
/** | |
* Produce printable stringly representation of class layout. | |
* This method accepts instance to read the actual data from. | |
* | |
* @param instance instance to work on | |
* @return human-readable layout info | |
*/ | |
public String toPrintable(Object instance) { | |
StringWriter sw = new StringWriter(); | |
PrintWriter pw = new PrintWriter(sw); | |
int maxTypeLen = 5; | |
for (FieldLayout f : fields()) { | |
maxTypeLen = Math.max(f.typeClass().length(), maxTypeLen); | |
} | |
int maxDescrLen = 30; | |
for (FieldLayout f : fields()) { | |
maxDescrLen = Math.max((f.hostClass() + "" + f.name()).length(), maxDescrLen); | |
} | |
pw.println(classData.name() + " object internals:"); | |
pw.printf(" %6s %5s %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", "OFFSET", "SIZE", "TYPE", "DESCRIPTION", "VALUE"); | |
if (instance != null) { | |
if (detailedHeader) { | |
ObjectHeaderLayout.make().print(pw, instance, maxTypeLen, maxDescrLen); | |
} else { | |
for (long off = 0; off < headerSize(); off += 4) { | |
pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", off, 4, "", "(object header)", | |
toHex(VMSupport.U.getByte(instance, off + 0) & 0xFF) + " " + | |
toHex(VMSupport.U.getByte(instance, off + 1) & 0xFF) + " " + | |
toHex(VMSupport.U.getByte(instance, off + 2) & 0xFF) + " " + | |
toHex(VMSupport.U.getByte(instance, off + 3) & 0xFF) + " " + | |
"(" + | |
toBinary(VMSupport.U.getByte(instance, off + 0) & 0xFF) + " " + | |
toBinary(VMSupport.U.getByte(instance, off + 1) & 0xFF) + " " + | |
toBinary(VMSupport.U.getByte(instance, off + 2) & 0xFF) + " " + | |
toBinary(VMSupport.U.getByte(instance, off + 3) & 0xFF) + ")" | |
); | |
} | |
} | |
} else { | |
pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", 0, headerSize(), "", "(object header)", "N/A"); | |
} | |
int nextFree = headerSize(); | |
int interLoss = 0; | |
int exterLoss = 0; | |
for (FieldLayout f : fields()) { | |
if (f.offset() > nextFree) { | |
pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", nextFree, (f.offset() - nextFree), "", "(alignment/padding gap)", "N/A"); | |
interLoss += (f.offset() - nextFree); | |
} | |
pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", | |
f.offset(), | |
f.size(), | |
f.typeClass(), | |
f.hostClass() + "." + f.name(), | |
(instance != null) ? f.safeValue(instance) : "N/A" | |
); | |
nextFree = f.offset() + f.size(); | |
} | |
VMSupport.SizeInfo info = VMSupport.tryExactObjectSize(instance, this); | |
if (info.instanceSize() != nextFree) { | |
exterLoss = info.instanceSize() - nextFree; | |
pw.printf(" %6d %5s %" + maxTypeLen + "s %s%n", nextFree, exterLoss, "", "(loss due to the next object alignment)"); | |
} | |
if (info.exactSize()) { | |
pw.printf("Instance size: %d bytes (reported by VM agent)%n", info.instanceSize()); | |
} else { | |
if (instance != null) { | |
pw.printf("Instance size: %d bytes (estimated, add this JAR via -javaagent: to get accurate result)%n", info.instanceSize()); | |
} else { | |
pw.printf("Instance size: %d bytes (estimated, the sample instance is not available)%n", info.instanceSize()); | |
} | |
} | |
pw.printf("Space losses: %d bytes internal + %d bytes external = %d bytes total%n", interLoss, exterLoss, interLoss + exterLoss); | |
pw.close(); | |
return sw.toString(); | |
} | |
// very ineffective, so what? | |
private static String toBinary(int x) { | |
String s = Integer.toBinaryString(x); | |
int deficit = 8 - s.length(); | |
for (int c = 0; c < deficit; c++) { | |
s = "0" + s; | |
} | |
return s.substring(0, 4) + " " + s.substring(4); | |
} | |
// very ineffective, so what? | |
private static String toHex(int x) { | |
String s = Integer.toHexString(x); | |
int deficit = 2 - s.length(); | |
for (int c = 0; c < deficit; c++) { | |
s = "0" + s; | |
} | |
return s; | |
} | |
public ClassLayout withDetailedHeader() { | |
return new ClassLayout(classData, fields, headerSize, size, false, true); | |
} | |
private static class ObjectHeaderLayout { | |
public static ObjectHeaderLayout make() { | |
switch (VMSupport.ADDRESS_SIZE) { | |
case 8: | |
return new ObjectHeaderLayout(Arrays.asList(new Entry(2, "lock"), | |
new Entry(1, "biased lock"), | |
new Entry(4, "age"), | |
new Entry(1, "(unused)"), | |
new Entry(31, "hash code"), | |
new Entry(25, "(unused)"), | |
new Entry(VMSupport.USE_COMPRESSED_REFS ? 32 : 64, "class pointer"))); | |
case 4: | |
return new ObjectHeaderLayout(Arrays.asList(new Entry(2, "lock"), | |
new Entry(1, "biased lock"), | |
new Entry(4, "age"), | |
new Entry(25, "hash code"), | |
new Entry(32, "class pointer"))); | |
default: | |
throw new IllegalStateException("Unknown address size: " + VMSupport.ADDRESS_SIZE); | |
} | |
} | |
public void print(PrintWriter pw, Object instance, int maxTypeLen, int maxDescrLen) { | |
int bitOffset = 0; | |
for (Entry entry : entries) { | |
int rangeValue = 0; | |
int valueOffset = 0; | |
for (long byteOffset = bitOffset / 8; | |
byteOffset < (bitOffset + entry.bitLength) / 8 + ((bitOffset + entry.bitLength) % 8 == 0 ? 0 : 1); | |
byteOffset++) { | |
int currentByte = VMSupport.U.getByte(instance, byteOffset) & 0xFF; | |
if (byteOffset * 8 < bitOffset) { | |
currentByte &= 0xFF >> (bitOffset % 8); | |
} | |
if (byteOffset * 8 + 8 > bitOffset + entry.bitLength) { | |
currentByte &= (0xFF << (8 - ((bitOffset + entry.bitLength) % 8))) & 0xFF; | |
} | |
rangeValue |= currentByte << (valueOffset * 8); | |
valueOffset++; | |
} | |
pw.printf(" %6s %5s %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", | |
bitOffset / 8 + "+" + bitOffset % 8, entry.bitLength / 8 + "+" + entry.bitLength % 8, "", entry.name, | |
Integer.toHexString(rangeValue) + " (" + Integer.toBinaryString(rangeValue) + ")"); | |
bitOffset += entry.bitLength; | |
} | |
} | |
private final List<Entry> entries; | |
private ObjectHeaderLayout(List<Entry> entries) { | |
this.entries = entries; | |
} | |
private static class Entry { | |
private final int bitLength; | |
private final String name; | |
private Entry(int bitLength, String name) { | |
this.bitLength = bitLength; | |
this.name = name; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 31:9a546334aa57fa0e114db159b2a8a5345c249b3e) | |
+++ jol-core/src/main/java/org/openjdk/jol/info/ClassLayout.java (revision 31+:9a546334aa57+) | |
@@ -30,6 +30,8 @@ | |
import java.io.PrintWriter; | |
import java.io.StringWriter; | |
+import java.util.Arrays; | |
+import java.util.List; | |
import java.util.SortedSet; | |
/** | |
@@ -64,6 +66,8 @@ | |
private final int headerSize; | |
private final int size; | |
+ private final boolean detailedHeader; | |
+ | |
/** | |
* Builds the class layout. | |
* | |
@@ -78,11 +82,23 @@ | |
this.fields = fields; | |
this.headerSize = headerSize; | |
this.size = instanceSize; | |
+ detailedHeader = false; | |
if (check) { | |
checkInvariants(); | |
} | |
} | |
+ private ClassLayout(ClassData classData, SortedSet<FieldLayout> fields, int headerSize, int instanceSize, boolean check, boolean detailedHeader) { | |
+ this.classData = classData; | |
+ this.fields = fields; | |
+ this.headerSize = headerSize; | |
+ this.size = instanceSize; | |
+ this.detailedHeader = detailedHeader; | |
+ if (check) { | |
+ checkInvariants(); | |
+ } | |
+ } | |
+ | |
private void checkInvariants() { | |
long lastOffset = 0; | |
for (FieldLayout f : fields) { | |
@@ -170,19 +186,23 @@ | |
pw.println(classData.name() + " object internals:"); | |
pw.printf(" %6s %5s %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", "OFFSET", "SIZE", "TYPE", "DESCRIPTION", "VALUE"); | |
if (instance != null) { | |
+ if (detailedHeader) { | |
+ ObjectHeaderLayout.make().print(pw, instance, maxTypeLen, maxDescrLen); | |
+ } else { | |
- for (long off = 0; off < headerSize(); off += 4) { | |
- pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", off, 4, "", "(object header)", | |
- toHex(VMSupport.U.getByte(instance, off + 0) & 0xFF) + " " + | |
- toHex(VMSupport.U.getByte(instance, off + 1) & 0xFF) + " " + | |
- toHex(VMSupport.U.getByte(instance, off + 2) & 0xFF) + " " + | |
- toHex(VMSupport.U.getByte(instance, off + 3) & 0xFF) + " " + | |
- "(" + | |
- toBinary(VMSupport.U.getByte(instance, off + 0) & 0xFF) + " " + | |
- toBinary(VMSupport.U.getByte(instance, off + 1) & 0xFF) + " " + | |
- toBinary(VMSupport.U.getByte(instance, off + 2) & 0xFF) + " " + | |
- toBinary(VMSupport.U.getByte(instance, off + 3) & 0xFF) + ")" | |
- ); | |
- } | |
+ for (long off = 0; off < headerSize(); off += 4) { | |
+ pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", off, 4, "", "(object header)", | |
+ toHex(VMSupport.U.getByte(instance, off + 0) & 0xFF) + " " + | |
+ toHex(VMSupport.U.getByte(instance, off + 1) & 0xFF) + " " + | |
+ toHex(VMSupport.U.getByte(instance, off + 2) & 0xFF) + " " + | |
+ toHex(VMSupport.U.getByte(instance, off + 3) & 0xFF) + " " + | |
+ "(" + | |
+ toBinary(VMSupport.U.getByte(instance, off + 0) & 0xFF) + " " + | |
+ toBinary(VMSupport.U.getByte(instance, off + 1) & 0xFF) + " " + | |
+ toBinary(VMSupport.U.getByte(instance, off + 2) & 0xFF) + " " + | |
+ toBinary(VMSupport.U.getByte(instance, off + 3) & 0xFF) + ")" | |
+ ); | |
+ } | |
+ } | |
} else { | |
pw.printf(" %6d %5d %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", 0, headerSize(), "", "(object header)", "N/A"); | |
} | |
@@ -253,4 +273,74 @@ | |
return s; | |
} | |
+ public ClassLayout withDetailedHeader() { | |
+ return new ClassLayout(classData, fields, headerSize, size, false, true); | |
+ } | |
+ | |
+ private static class ObjectHeaderLayout { | |
+ | |
+ public static ObjectHeaderLayout make() { | |
+ switch (VMSupport.ADDRESS_SIZE) { | |
+ case 8: | |
+ return new ObjectHeaderLayout(Arrays.asList(new Entry(2, "lock"), | |
+ new Entry(1, "biased lock"), | |
+ new Entry(4, "age"), | |
+ new Entry(1, "(unused)"), | |
+ new Entry(31, "hash code"), | |
+ new Entry(25, "(unused)"), | |
+ new Entry(VMSupport.USE_COMPRESSED_REFS ? 32 : 64, "class pointer"))); | |
+ case 4: | |
+ return new ObjectHeaderLayout(Arrays.asList(new Entry(2, "lock"), | |
+ new Entry(1, "biased lock"), | |
+ new Entry(4, "age"), | |
+ new Entry(25, "hash code"), | |
+ new Entry(32, "class pointer"))); | |
+ default: | |
+ throw new IllegalStateException("Unknown address size: " + VMSupport.ADDRESS_SIZE); | |
+ } | |
+ } | |
+ | |
+ public void print(PrintWriter pw, Object instance, int maxTypeLen, int maxDescrLen) { | |
+ int bitOffset = 0; | |
+ for (Entry entry : entries) { | |
+ int rangeValue = 0; | |
+ int valueOffset = 0; | |
+ for (long byteOffset = bitOffset / 8; | |
+ byteOffset < (bitOffset + entry.bitLength) / 8 + ((bitOffset + entry.bitLength) % 8 == 0 ? 0 : 1); | |
+ byteOffset++) { | |
+ int currentByte = VMSupport.U.getByte(instance, byteOffset) & 0xFF; | |
+ if (byteOffset * 8 < bitOffset) { | |
+ currentByte &= 0xFF >> (bitOffset % 8); | |
+ } | |
+ if (byteOffset * 8 + 8 > bitOffset + entry.bitLength) { | |
+ currentByte &= (0xFF << (8 - ((bitOffset + entry.bitLength) % 8))) & 0xFF; | |
+ } | |
+ rangeValue |= currentByte << (valueOffset * 8); | |
+ valueOffset++; | |
+ } | |
+ pw.printf(" %6s %5s %" + maxTypeLen + "s %-" + maxDescrLen + "s %s%n", | |
+ bitOffset / 8 + "+" + bitOffset % 8, entry.bitLength / 8 + "+" + entry.bitLength % 8, "", entry.name, | |
+ Integer.toHexString(rangeValue) + " (" + Integer.toBinaryString(rangeValue) + ")"); | |
+ bitOffset += entry.bitLength; | |
+ } | |
+ } | |
+ | |
+ private final List<Entry> entries; | |
+ | |
+ private ObjectHeaderLayout(List<Entry> entries) { | |
+ this.entries = entries; | |
+ } | |
+ | |
+ private static class Entry { | |
+ | |
+ private final int bitLength; | |
+ | |
+ private final String name; | |
+ | |
+ private Entry(int bitLength, String name) { | |
+ this.bitLength = bitLength; | |
+ this.name = name; | |
+ } | |
+ } | |
+ } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment