Add compact-strings module: Compact Strings (JEP 254) measured with JOL on JDK 25, with and without -XX:-CompactStrings, and compact object headers on 25 vs 27

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KqJyCidz3ZgRyHABv2GVJh
This commit is contained in:
2026-10-01 04:23:27 +00:00
co-authored by Claude Sonnet 5.5
parent fadfd05670
commit fc05eed4b5
26 changed files with 943 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
import java.lang.reflect.Field;
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.info.GraphLayout;
import org.openjdk.jol.vm.VM;
/**
* What one String really costs: the String object, the byte[] it points to, and the coder byte that says how to read it.
* Measured with JOL (org.openjdk.jol:jol-core). Run by run.sh under several JVM flag sets.
*/
public class StringLayout {
static byte coder(String s) throws Exception {
Field f = String.class.getDeclaredField("coder");
f.setAccessible(true); // needs --add-opens java.base/java.lang=ALL-UNNAMED
return f.getByte(s);
}
static byte[] value(String s) throws Exception {
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
return (byte[]) f.get(s);
}
static String repeat(char c, int n) { return String.valueOf(c).repeat(n); }
public static void main(String[] args) throws Exception {
System.out.println("java.version = " + System.getProperty("java.version"));
System.out.println("vm.flags = " + String.join(" ", java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments()
.stream().filter(a -> a.startsWith("-XX")).toList()));
System.out.println(VM.current().details().lines().filter(l -> l.startsWith("# Objects are 8 bytes") || l.startsWith("# Field sizes")
|| l.contains("Array element") || l.contains("compressed")).reduce("", (a, b) -> a + b + "\n"));
System.out.println("=== java.lang.String, the class");
System.out.println(ClassLayout.parseClass(String.class).toPrintable());
System.out.println("=== one Latin-1 string and one UTF-16 string, field by field");
System.out.println(ClassLayout.parseInstance("hello").toPrintable());
System.out.println(ClassLayout.parseInstance("h\u20ACllo").toPrintable());
System.out.println("=== the byte[] behind each");
System.out.println(ClassLayout.parseInstance(value("hello")).toPrintable());
System.out.println(ClassLayout.parseInstance(value("h\u20ACllo")).toPrintable());
System.out.println("=== footprint by length (String object + its byte[], via GraphLayout)");
System.out.println("chars content coder byte[].length String byte[] total");
for (int n : new int[] {0, 1, 5, 10, 20, 40, 100, 1000}) {
for (char c : new char[] {'a', '\u00E9', '\u20AC'}) { // a = ASCII, e-acute = Latin-1 supplement, euro = needs UTF-16
String s = new String(repeat(c, n).toCharArray()); // a fresh object, never the interned literal
long total = GraphLayout.parseInstance(s).totalSize();
long str = ClassLayout.parseInstance(s).instanceSize();
long arr = ClassLayout.parseInstance(value(s)).instanceSize();
System.out.printf("%5d %-8s %5d %13d %6d %6d %5d%n", n,
c == 'a' ? "ASCII" : c == '\u00E9' ? "U+00E9" : "U+20AC", coder(s), value(s).length, str, arr, total);
}
}
}
}