1 /* 2 * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2025, Red Hat, Inc. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 import jdk.internal.misc.Unsafe; 26 import jdk.test.whitebox.WhiteBox; 27 28 import java.lang.reflect.Field; 29 import java.util.ArrayList; 30 import java.util.List; 31 32 /* 33 * @test id=no_coops_no_ccptr_no_coh 34 * @library /test/lib 35 * @modules java.base/jdk.internal.misc 36 * java.management 37 * @build jdk.test.whitebox.WhiteBox 38 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 39 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:-UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal 40 */ 41 42 /* 43 * @test id=coops_no_ccptr_no_coh 44 * @library /test/lib 45 * @modules java.base/jdk.internal.misc 46 * java.management 47 * @build jdk.test.whitebox.WhiteBox 48 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 49 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:-UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal 50 */ 51 52 /* 53 * @test id=no_coops_ccptr_no_coh 54 * @library /test/lib 55 * @modules java.base/jdk.internal.misc 56 * java.management 57 * @build jdk.test.whitebox.WhiteBox 58 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 59 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:+UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal 60 */ 61 62 /* 63 * @test id=coops_ccptr_no_coh 64 * @library /test/lib 65 * @modules java.base/jdk.internal.misc 66 * java.management 67 * @build jdk.test.whitebox.WhiteBox 68 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 69 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedOops -XX:+UseCompressedClassPointers -XX:-UseCompactObjectHeaders TestOopMapSizeMinimal 70 */ 71 72 /* 73 * @test id=no_coops_ccptr_coh 74 * @library /test/lib 75 * @modules java.base/jdk.internal.misc 76 * java.management 77 * @build jdk.test.whitebox.WhiteBox 78 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 79 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UnlockExperimentalVMOptions -XX:-UseCompressedOops -XX:+UseCompactObjectHeaders TestOopMapSizeMinimal 80 */ 81 82 public class TestOopMapSizeMinimal { 83 84 public static int OOP_SIZE_IN_BYTES = -1; 85 public static int HEADER_SIZE_IN_BYTES = -1; 86 87 static { 88 WhiteBox WB = WhiteBox.getWhiteBox(); 89 boolean is_64_bit = System.getProperty("sun.arch.data.model").equals("64"); 90 if (is_64_bit) { 91 if (System.getProperty("java.vm.compressedOopsMode") == null) { 92 OOP_SIZE_IN_BYTES = 8; 93 } else { 94 OOP_SIZE_IN_BYTES = 4; 95 } 96 } else { 97 OOP_SIZE_IN_BYTES = 4; 98 } 99 if (is_64_bit) { 100 if (WB.getBooleanVMFlag("UseCompactObjectHeaders")) { 101 HEADER_SIZE_IN_BYTES = 8; 102 } else if (WB.getBooleanVMFlag("UseCompressedClassPointers")) { 103 HEADER_SIZE_IN_BYTES = 12; 104 } else { 105 HEADER_SIZE_IN_BYTES = 16; 106 } 107 } else { 108 HEADER_SIZE_IN_BYTES = 8; 109 } 110 } 111 112 public static long alignUp(long value, long alignment) { 113 return (value + alignment - 1) & ~(alignment - 1); 114 } 115 116 public static long alignForOop(long position) { 117 return alignUp(position, OOP_SIZE_IN_BYTES); 118 } 119 120 private static final Unsafe U = Unsafe.getUnsafe(); 121 122 public static class BASE { 123 int i1; 124 Object o1; 125 } 126 127 public static class DERIVED1 extends BASE { 128 int i2; 129 Object o2; 130 } 131 132 public static class DERIVED2 extends DERIVED1 { 133 int i3; 134 Object o3; 135 } 136 137 public static class DERIVED3 extends DERIVED2 { 138 int i4; 139 Object o4; 140 } 141 142 static boolean mismatch = false; 143 144 private static void checkOffset(Field f, long expectedOffset) { 145 long realOffset = U.objectFieldOffset(f); 146 System.out.println("Offset for field " + f.getName() + 147 ": expected " + expectedOffset + ", got " + realOffset + "."); 148 if (U.objectFieldOffset(f) != expectedOffset) { 149 mismatch = true; 150 System.out.println(" ... mimatch"); 151 } 152 } 153 154 private static List<Field> allFieldsOf(Class c) { 155 ArrayList<Field> l = new ArrayList<>(); 156 while (c != null) { 157 for (Field f : c.getDeclaredFields()) { 158 l.add(f); 159 } 160 c = c.getSuperclass(); 161 } 162 return l; 163 } 164 165 public static void main(String[] args) throws Exception { 166 167 System.out.println("HEADER_SIZE_IN_BYTES " + HEADER_SIZE_IN_BYTES + ", OOP_SIZE_IN_BYTES " + OOP_SIZE_IN_BYTES); 168 169 long i1_loc_expected; 170 long o1_loc_expected; 171 long o2_loc_expected; 172 long i2_loc_expected; 173 long i3_loc_expected; 174 long o3_loc_expected; 175 long o4_loc_expected; 176 long i4_loc_expected; 177 178 // We expect the layouter to reverse order of oop- and non-oop fields 179 // when it is useful to minimize the number of oop map entries. 180 // 181 // If we have no gaps, this should be the layout: 182 // BASE i1 183 // o1 oopmap entry 1 184 // DERIVED1 o2 oopmap entry 1 (reversed order) 185 // i2 186 // DERIVED3 i3 187 // o3 oopmap entry 2 188 // DERIVED4 o4 oopmap entry 2 (reversed order) 189 // i4 190 191 // There are two combinations that have gaps: 192 // -UseCompressedOops + +COH, and -UseCompressedOops + -UseCompressedClassPointers. 193 // In both cases there is a gap following i1, and i2 will therefore nestle into that gap. 194 // Otherwise the same logic applies. 195 196 if (OOP_SIZE_IN_BYTES == 4 || // oop size == int size 197 (OOP_SIZE_IN_BYTES == 8 && HEADER_SIZE_IN_BYTES == 12) 198 ) { 199 // No gaps 200 201 // Expected layout for BASE: int, object 202 i1_loc_expected = HEADER_SIZE_IN_BYTES; 203 o1_loc_expected = i1_loc_expected + 4; 204 205 // Expected layout for DERIVED1: object, int (to make o2 border o1) 206 o2_loc_expected = o1_loc_expected + OOP_SIZE_IN_BYTES; 207 i2_loc_expected = o2_loc_expected + OOP_SIZE_IN_BYTES; 208 209 // Expected layout for DERIVED2: int, object (to trail with oops, for derived classes to nestle against) 210 i3_loc_expected = i2_loc_expected + 4; 211 o3_loc_expected = i3_loc_expected + 4; 212 213 // Expected layout for DERIVED3: object, int (to make o4 border o3) 214 o4_loc_expected = o3_loc_expected + OOP_SIZE_IN_BYTES; 215 i4_loc_expected = o4_loc_expected + OOP_SIZE_IN_BYTES; 216 217 } else if (OOP_SIZE_IN_BYTES == 8) { 218 219 // gap after i1 220 221 i1_loc_expected = HEADER_SIZE_IN_BYTES; 222 o1_loc_expected = i1_loc_expected + 4 + 4; // + alignment gap 223 224 o2_loc_expected = o1_loc_expected + OOP_SIZE_IN_BYTES; 225 i2_loc_expected = i1_loc_expected + 4; // into gap following i1 226 227 o3_loc_expected = o2_loc_expected + OOP_SIZE_IN_BYTES; 228 i3_loc_expected = o3_loc_expected + OOP_SIZE_IN_BYTES; 229 230 i4_loc_expected = i3_loc_expected + 4; 231 o4_loc_expected = i4_loc_expected + 4; 232 } else { 233 throw new RuntimeException("Unexpected"); 234 } 235 236 List<Field> l = allFieldsOf(DERIVED3.class); 237 for (Field f : l) { 238 switch (f.getName()) { 239 case "i1" : checkOffset(f, i1_loc_expected); break; 240 case "o1" : checkOffset(f, o1_loc_expected); break; 241 case "i2" : checkOffset(f, i2_loc_expected); break; 242 case "o2" : checkOffset(f, o2_loc_expected); break; 243 case "i3" : checkOffset(f, i3_loc_expected); break; 244 case "o3" : checkOffset(f, o3_loc_expected); break; 245 case "i4" : checkOffset(f, i4_loc_expected); break; 246 case "o4" : checkOffset(f, o4_loc_expected); break; 247 default: throw new RuntimeException("Unexpected"); 248 } 249 } 250 if (mismatch) { 251 throw new RuntimeException("Mismatch!"); 252 } 253 System.out.println("All good."); 254 } 255 256 } 257