1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package runtime.valhalla.valuetypes; 25 26 import java.lang.invoke.*; 27 import java.lang.ref.*; 28 import java.util.concurrent.*; 29 30 import jdk.experimental.value.MethodHandleBuilder; 31 import jdk.incubator.mvt.ValueType; 32 33 import static jdk.test.lib.Asserts.*; 34 import jdk.test.lib.Utils; 35 import sun.hotspot.WhiteBox; 36 37 /** 38 * @test ValueOops 39 * @summary Test embedding oops into Value types 40 * @modules java.base/jdk.experimental.value 41 * jdk.incubator.mvt 42 * @library /test/lib 43 * @compile PersonVcc.java 44 * @compile -XDenableValueTypes Person.java 45 * @compile -XDenableValueTypes ValueOops.java 46 * @run main ClassFileInstaller sun.hotspot.WhiteBox 47 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 48 * sun.hotspot.WhiteBox$WhiteBoxPermission 49 * @run main/othervm -Xint -noverify -XX:+UseSerialGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 50 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 51 * runtime.valhalla.valuetypes.ValueOops 52 * @run main/othervm -Xint -noverify -XX:+UseG1GC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 53 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 54 * runtime.valhalla.valuetypes.ValueOops 55 * @run main/othervm -Xint -noverify -XX:+UseParallelGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 56 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 57 * runtime.valhalla.valuetypes.ValueOops 58 * @run main/othervm -Xint -noverify -XX:+UseConcMarkSweepGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 59 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 60 * runtime.valhalla.valuetypes.ValueOops 61 * @run main/othervm -Xcomp -noverify -XX:+UseSerialGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 62 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 63 * runtime.valhalla.valuetypes.ValueOops 64 * @run main/othervm -Xcomp -noverify -XX:+UseG1GC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 65 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 66 * runtime.valhalla.valuetypes.ValueOops 67 * @run main/othervm -Xcomp -noverify -XX:+UseParallelGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 68 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 69 * runtime.valhalla.valuetypes.ValueOops 70 * @run main/othervm -Xcomp -noverify -XX:+UseConcMarkSweepGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 71 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 72 * runtime.valhalla.valuetypes.ValueOops 73 */ 74 public class ValueOops { 75 76 // Note: -noverify can not be eliminated. Possible issue with ValueType::mhName(), 77 // does not translate '.' to '/' for JVM. 78 // java.lang.ClassFormatError: Illegal method name "runtime.valhalla.valuetypes.PersonVcc_default" 79 // 80 // Extra debug: -XX:+VerifyOops -XX:+VerifyStack -XX:+VerifyLastFrame -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:+VerifyDuringGC -XX:VerifySubSet=threads,heap 81 // Even more debugging: -XX:+TraceNewOopMapGeneration -Xlog:gc*=info 82 83 static final int NOF_PEOPLE = 1000; // Exercise arrays of this size 84 85 static int MIN_ACTIVE_GC_COUNT = 10; // Run active workload for this number of GC passes 86 87 static int MED_ACTIVE_GC_COUNT = 4; // Medium life span in terms of GC passes 88 89 static final String TEST_STRING1 = "Test String 1"; 90 static final String TEST_STRING2 = "Test String 2"; 91 92 static boolean USE_COMPILER = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompiler"); 93 94 public static void main(String[] args) { 95 if (args.length > 0) { 96 MIN_ACTIVE_GC_COUNT = Integer.parseInt(args[0]); 97 } 98 testClassLoad(); 99 testBytecodes(); 100 testVvt(); 101 testMvt(); 102 103 if (!USE_COMPILER) { 104 testOopMaps(); 105 } 106 107 // Check we survive GC... 108 testOverGc(); // Exercise root scan / oopMap 109 testActiveGc(); // Brute force 110 } 111 112 /** 113 * Test ClassFileParser can load values with reference fields 114 */ 115 public static void testClassLoad() { 116 // VVT 117 String s = Person.class.toString(); 118 new Bar(); 119 new BarWithValue(); 120 s = BarValue.class.toString(); 121 s = ObjectWithObjectValue.class.toString(); 122 s = ObjectWithObjectValues.class.toString(); 123 124 // MVT 125 Class<?> vccClass = PersonVcc.class; 126 ValueType<?> vt = ValueType.forClass(vccClass); 127 Class<?> boxClass = vt.boxClass(); 128 Class<?> dvtClass = vt.valueClass(); 129 Class<?> arrayClass = vt.arrayValueClass(); 130 s = dvtClass.toString(); 131 } 132 133 /** 134 * Test value type opcodes are okay with reference fields in values 135 * I.e. ValueKlass::value_store() 136 */ 137 public static void testBytecodes() { 138 try { 139 // Craft Value Type class how you will, using MVT class for simplicity just here 140 ValueType<?> vt = ValueType.forClass(PersonVcc.class); 141 Class<?> vtClass = vt.valueClass(); 142 Class<?> arrayClass = vt.arrayValueClass(); 143 Class<?> boxClass = vt.boxClass(); 144 MethodHandles.Lookup lookup = MethodHandles.lookup(); 145 146 // Exercise with null refs... 147 148 // anewarray 149 Object array = MethodHandleBuilder.loadCode( 150 lookup, 151 "anewarrayPerson", 152 MethodType.methodType(Object.class, Integer.TYPE), 153 CODE->{ 154 CODE 155 .iload(0) 156 .anewarray(vtClass) 157 .astore(0) 158 .aload(0) 159 .areturn(); 160 }).invoke(NOF_PEOPLE); 161 162 // vaload 163 // vstore 164 // vload 165 // vastore 166 // vbox 167 int arraySlot = 0; 168 int indexSlot = 1; 169 int valueSlot = 2; 170 Object obj = MethodHandleBuilder.loadCode( 171 lookup, 172 "valoadBoxPerson", 173 MethodType.methodType(Object.class, arrayClass, Integer.TYPE), 174 CODE->{ 175 CODE 176 .aload(arraySlot) 177 .iload(indexSlot) 178 .vaload() 179 .vstore(valueSlot) 180 .aload(arraySlot) 181 .iload(indexSlot) 182 .vload(valueSlot) 183 .vastore() 184 .vload(valueSlot) 185 .vbox(boxClass) 186 .areturn(); 187 }).invoke(array, 7); 188 validateDefaultPersonVcc(obj); 189 190 // vreturn 191 MethodHandle loadValueFromArray = MethodHandleBuilder.loadCode( 192 lookup, 193 "valoadVreturnPerson", 194 MethodType.methodType(vtClass, arrayClass, Integer.TYPE), 195 CODE->{ 196 CODE 197 .aload(arraySlot) 198 .iload(indexSlot) 199 .vaload() 200 .vreturn(); 201 }); 202 MethodHandle box = MethodHandleBuilder.loadCode( 203 lookup, 204 "boxPersonVcc", 205 MethodType.methodType(boxClass, vtClass), 206 CODE->{ 207 CODE 208 .vload(0) 209 .vbox(boxClass) 210 .areturn(); 211 }); 212 MethodHandle loadValueFromArrayBoxed = 213 MethodHandles.filterReturnValue(loadValueFromArray, box); 214 obj = loadValueFromArrayBoxed.invoke(array, 0); 215 validateDefaultPersonVcc(obj); 216 217 // vunbox 218 MethodHandle unbox = MethodHandleBuilder.loadCode( 219 lookup, 220 "unboxPersonVcc", 221 MethodType.methodType(vtClass, boxClass), 222 CODE->{ 223 CODE 224 .aload(0) 225 .vunbox(vtClass) 226 .vreturn(); 227 }); 228 MethodHandle unboxBox = MethodHandles.filterReturnValue(unbox, box); 229 obj = unboxBox.invoke(createDefaultPersonVcc()); 230 validateDefaultPersonVcc(obj); 231 232 /* 233 vgetfield 234 qputfield 235 qgetfield 236 237 going to need VVT for VT fields and vgetfield 238 check test coverage in testVvt() 239 */ 240 241 // Exercise with live refs... 242 Thread.holdsLock("Debug here"); 243 // vunbox 244 // vastore 245 // vaload 246 // vstore 247 // vload 248 // vbox 249 int index = 3; 250 obj = MethodHandleBuilder.loadCode( 251 lookup, 252 "unboxStoreLoadPersonVcc", 253 MethodType.methodType(boxClass, arrayClass, Integer.TYPE, boxClass), 254 CODE->{ 255 CODE 256 .aload(arraySlot) 257 .iload(indexSlot) 258 .aload(valueSlot) 259 .vunbox(vtClass) 260 .vastore() 261 .aload(arraySlot) 262 .iload(indexSlot) 263 .vaload() 264 .vstore(valueSlot) 265 .vload(valueSlot) 266 .vbox(boxClass) 267 .areturn(); 268 }).invoke(array, index, createIndexedPersonVcc(index)); 269 validateIndexedPersonVcc(obj, index); 270 271 // Check the neighbours 272 validateDefaultPersonVcc(loadValueFromArrayBoxed.invoke(array, index - 1)); 273 validateDefaultPersonVcc(loadValueFromArrayBoxed.invoke(array, index + 1)); 274 275 // vreturn 276 validateIndexedPersonVcc(unboxBox.invoke((PersonVcc)obj), index); 277 } 278 catch (Throwable t) { fail("testBytecodes", t); } 279 } 280 281 static class Couple { 282 public Person onePerson; 283 public Person otherPerson; 284 } 285 286 static final __ByValue class Composition { 287 public final Person onePerson; 288 public final Person otherPerson; 289 290 private Composition() { 291 this.onePerson = Person.create(0, null, null); 292 this.otherPerson = Person.create(0, null, null); 293 } 294 295 __ValueFactory public static Composition create(Person onePerson, Person otherPerson) { 296 Composition comp = __MakeDefault Composition(); 297 comp.onePerson = onePerson; 298 comp.otherPerson = otherPerson; 299 return comp; 300 } 301 } 302 303 /** 304 * Check value type operations with "Valhalla Value Types" (VVT) 305 */ 306 public static void testVvt() { 307 // Exercise creation, vgetfield, vreturn with null refs 308 validateDefaultPerson(createDefaultPerson()); 309 310 // anewarray, vaload, vastore 311 int index = 7; 312 Person[] array = new Person[NOF_PEOPLE]; 313 validateDefaultPerson(array[index]); 314 315 // Now with refs... 316 validateIndexedPerson(createIndexedPerson(index), index); 317 array[index] = createIndexedPerson(index); 318 validateIndexedPerson(array[index], index); 319 320 // Check the neighbours 321 validateDefaultPerson(array[index - 1]); 322 validateDefaultPerson(array[index + 1]); 323 324 // getfield/putfield 325 Couple couple = new Couple(); 326 validateDefaultPerson(couple.onePerson); 327 validateDefaultPerson(couple.otherPerson); 328 329 couple.onePerson = createIndexedPerson(index); 330 validateIndexedPerson(couple.onePerson, index); 331 332 Composition composition = Composition.create(couple.onePerson, couple.onePerson); 333 validateIndexedPerson(composition.onePerson, index); 334 validateIndexedPerson(composition.otherPerson, index); 335 } 336 337 /** 338 * Check value type operations with "Minimal Value Types" (MVT) 339 */ 340 public static void testMvt() { 341 try { 342 // MVT... 343 ValueType<?> vt = ValueType.forClass(PersonVcc.class); 344 Class<?> vtClass = vt.valueClass(); 345 Class<?> arrayClass = vt.arrayValueClass(); 346 347 Object obj = vt.defaultValueConstant().invoke(); 348 validateDefaultPersonVcc(obj); 349 350 obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box()) 351 .invoke(createDefaultPersonVcc()); 352 validateDefaultPersonVcc(obj); 353 354 int index = 11; 355 obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box()) 356 .invoke(createIndexedPersonVcc(index)); 357 validateIndexedPersonVcc(obj, index); 358 359 testMvtArray("testMvt.array.1", 1); 360 } 361 catch (Throwable t) { 362 fail("testMvtfailed", t); 363 } 364 } 365 366 /** 367 * MVT array operations... 368 */ 369 public static void testMvtPeopleArray() { 370 testMvtArray("testMvtPeopleArray", NOF_PEOPLE); 371 } 372 373 public static void testMvtArray(String testName, int arrayLength) { 374 try { 375 Class<?> vcc = PersonVcc.class; 376 ValueType<?> vt = ValueType.forClass(vcc); 377 Class<?> dvtClass = vt.valueClass(); 378 Class<?> arrayClass = vt.arrayValueClass(); 379 380 MethodHandle arrayElemGet = MethodHandles.arrayElementGetter(arrayClass); 381 MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(arrayClass); 382 383 MethodHandles.Lookup lookup = MethodHandles.lookup(); 384 MethodHandle getId = lookup.findGetter(dvtClass, "id", Integer.TYPE); 385 MethodHandle getFirstName = lookup.findGetter(dvtClass, "firstName", String.class); 386 MethodHandle getLastName = lookup.findGetter(dvtClass, "lastName", String.class); 387 388 MethodHandle getIdFromArray = MethodHandles.filterReturnValue(arrayElemGet, getId); 389 MethodHandle getFnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getFirstName); 390 MethodHandle getLnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getLastName); 391 392 Object people = vt.newArray().invoke(arrayLength); 393 for (int i = 0; i < arrayLength; i++) { 394 arrayElemSet.invoke(people, i, createIndexedPersonVcc(i)); 395 } 396 397 for (int i = 0; i < arrayLength; i++) { 398 validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i); 399 400 int id = (int) getIdFromArray.invoke(people, i); 401 assertTrue(id == i, "Invalid field: Id"); 402 String fn = (String) getFnFromArray.invoke(people, i); 403 assertTrue(fn.equals(firstName(i)), "Invalid field: firstName"); 404 String ln = (String) getLnFromArray.invoke(people, i); 405 assertTrue(ln.equals(lastName(i)), "Invalid field: lastName"); 406 } 407 } 408 catch (Throwable t) { 409 fail(testName + " failed", t); 410 } 411 } 412 413 /** 414 * Check oop map generation for klass layout and frame... 415 */ 416 public static void testOopMaps() { 417 Object[] objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(new Couple()); 418 assertTrue(objects.length == 4, "Expected 4 oops"); 419 for (int i = 0; i < objects.length; i++) { 420 assertTrue(objects[i] == null, "not-null"); 421 } 422 423 String fn1 = "Sam"; 424 String ln1 = "Smith"; 425 String fn2 = "Jane"; 426 String ln2 = "Jones"; 427 Couple couple = new Couple(); 428 couple.onePerson = Person.create(0, fn1, ln1); 429 couple.otherPerson = Person.create(1, fn2, ln2); 430 objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(couple); 431 assertTrue(objects.length == 4, "Expected 4 oops"); 432 assertTrue(objects[0] == fn1, "Bad oop fn1"); 433 assertTrue(objects[1] == ln1, "Bad oop ln1"); 434 assertTrue(objects[2] == fn2, "Bad oop fn2"); 435 assertTrue(objects[3] == ln2, "Bad oop ln2"); 436 437 objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(couple); 438 assertTrue(objects.length == 4, "Expected 4 oops"); 439 assertTrue(objects[0] == fn1, "Bad oop fn1"); 440 assertTrue(objects[1] == ln1, "Bad oop ln1"); 441 assertTrue(objects[2] == fn2, "Bad oop fn2"); 442 assertTrue(objects[3] == ln2, "Bad oop ln2"); 443 444 // Array.. 445 objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(createPeople()); 446 assertTrue(objects.length == NOF_PEOPLE * 2, "Unexpected length: " + objects.length); 447 int o = 0; 448 for (int i = 0; i < NOF_PEOPLE; i++) { 449 assertTrue(objects[o++].equals(firstName(i)), "Bad firstName"); 450 assertTrue(objects[o++].equals(lastName(i)), "Bad lastName"); 451 } 452 453 // Sanity check, FixMe need more test cases 454 objects = testFrameOops(couple); 455 assertTrue(objects.length == 5, "Number of frame oops incorrect = " + objects.length); 456 assertTrue(objects[0] == couple, "Bad oop 0"); 457 assertTrue(objects[1] == fn1, "Bad oop 1"); 458 assertTrue(objects[2] == ln1, "Bad oop 2"); 459 assertTrue(objects[3] == TEST_STRING1, "Bad oop 3"); 460 assertTrue(objects[4] == TEST_STRING2, "Bad oop 4"); 461 462 testFrameOopsVBytecodes(); 463 } 464 465 static final String GET_OOP_MAP_NAME = "getOopMap"; 466 static final String GET_OOP_MAP_DESC = "()[Ljava/lang/Object;"; 467 468 static Object[] getOopMap() { 469 Object[] oopMap = WhiteBox.getWhiteBox().getObjectsViaFrameOopIterator(2); 470 /* Remove this frame (class mirror for this method), and above class mirror */ 471 Object[] trimmedOopMap = new Object[oopMap.length - 2]; 472 System.arraycopy(oopMap, 2, trimmedOopMap, 0, trimmedOopMap.length); 473 return trimmedOopMap; 474 } 475 476 // Expecting Couple couple, Person couple.onePerson, and Person (created here) 477 public static Object[] testFrameOops(Couple couple) { 478 int someId = 89898; 479 Person person = couple.onePerson; 480 assertTrue(person.getId() == 0, "Bad Person"); 481 Person anotherPerson = Person.create(someId, TEST_STRING1, TEST_STRING2); 482 assertTrue(anotherPerson.getId() == someId, "Bad Person"); 483 return getOopMap(); 484 } 485 486 // Debug... 487 static void dumpOopMap(Object[] oopMap) { 488 System.out.println("Oop Map len: " + oopMap.length); 489 for (int i = 0; i < oopMap.length; i++) { 490 System.out.println("[" + i + "] = " + oopMap[i]); 491 } 492 } 493 494 /** 495 * Just some check sanity checks with vdefault, vwithfield, vstore and vload 496 * 497 * Changes to javac slot usage may well break this test 498 */ 499 public static void testFrameOopsVBytecodes() { 500 int nofOopMaps = 4; 501 Object[][] oopMaps = new Object[nofOopMaps][]; 502 String[] inputArgs = new String[] { "aName", "aDescription", "someNotes" }; 503 504 FooValue.testFrameOopsDefault(oopMaps); 505 506 dumpOopMap(oopMaps[0]); 507 dumpOopMap(oopMaps[1]); 508 dumpOopMap(oopMaps[2]); 509 dumpOopMap(oopMaps[3]); 510 511 // Test-D0 Slots=R Stack=Q(RRR)RV 512 assertTrue(oopMaps[0].length == 5 && 513 oopMaps[0][1] == null && 514 oopMaps[0][2] == null && 515 oopMaps[0][3] == null, "Test-D0 incorrect"); 516 517 // Test-D1 Slots=R Stack=RV 518 assertTrue(oopMaps[1].length == 2, "Test-D1 incorrect"); 519 520 // Test-D2 Slots=RQ(RRR) Stack=RV 521 assertTrue(oopMaps[2].length == 5 && 522 oopMaps[2][1] == null && 523 oopMaps[2][2] == null && 524 oopMaps[2][3] == null, "Test-D2 incorrect"); 525 526 // Test-D3 Slots=R Stack=Q(RRR)RV 527 assertTrue(oopMaps[3].length == 6 && 528 oopMaps[3][1] == null && 529 oopMaps[3][2] == null && 530 oopMaps[3][3] == null && 531 oopMaps[3][4] == null, "Test-D3 incorrect"); 532 533 // With ref fields... 534 String name = "TestName"; 535 String desc = "TestDesc"; 536 String note = "TestNotes"; 537 FooValue.testFrameOopsRefs(name, desc, note, oopMaps); 538 dumpOopMap(oopMaps[0]); 539 540 // Test-R0 Slots=RR Stack=Q(RRR)RV 541 assertTrue(oopMaps[0].length == 6 && 542 oopMaps[0][2] == name && 543 oopMaps[0][3] == desc && 544 oopMaps[0][4] == note, "Test-R0 incorrect"); 545 546 /** 547 * TODO: vwithfield from method handle cooked from anonymous class within the value class 548 * even with "MethodHandles.privateLookupIn()" will fail final putfield rules 549 */ 550 } 551 552 /** 553 * Check forcing GC for combination of VT on stack/LVT etc works 554 */ 555 public static void testOverGc() { 556 try { 557 Class<?> vccClass = PersonVcc.class; 558 ValueType<?> vt = ValueType.forClass(vccClass); 559 Class<?> vtClass = vt.valueClass(); 560 Class<?> arrayClass = vt.arrayValueClass(); 561 562 MethodHandles.Lookup lookup = MethodHandles.lookup(); 563 doGc(); 564 565 // VT on stack and lvt, null refs, see if GC flies 566 MethodHandle moveValueThroughStackAndLvt = MethodHandleBuilder.loadCode( 567 lookup, 568 "gcOverPerson", 569 MethodType.methodType(vccClass, vccClass), 570 CODE->{ 571 CODE 572 .aload(0) 573 .vunbox(vtClass) 574 .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack 575 .vstore(0) 576 .invokestatic(ValueOops.class, "doGc", "()V", false) // LVT 577 .vload(0) 578 .iconst_1() // push a litte further down 579 .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack,LVT 580 .pop() 581 .vbox(vccClass) 582 .areturn(); 583 }); 584 Object obj = moveValueThroughStackAndLvt.invoke(createDefaultPersonVcc()); 585 validateDefaultPersonVcc(obj); 586 doGc(); 587 obj = null; 588 doGc(); 589 590 int index = 4711; 591 obj = moveValueThroughStackAndLvt.invoke(createIndexedPersonVcc(index)); 592 validateIndexedPersonVcc(obj, index); 593 doGc(); 594 obj = null; 595 doGc(); 596 } 597 catch (Throwable t) { fail("testOverGc", t); } 598 } 599 600 static void submitNewWork(ForkJoinPool fjPool, int size) { 601 for (int i = 0; i < size; i++) { 602 fjPool.execute(ValueOops::testMvtPeopleArray); 603 for (int j = 0; j < 100; j++) { 604 // JDK-8186718 random crashes in interpreter vbox and vunbox (with G1) 605 // test needs refactoring to more specific use cases for debugging. 606 //fjPool.execute(ValueOops::testBytecodes); 607 fjPool.execute(ValueOops::testVvt); 608 fjPool.execute(ValueOops::testMvt); 609 } 610 } 611 } 612 613 static void sleepNoThrow(long ms) { 614 try { 615 Thread.sleep(ms); 616 } 617 catch (Throwable t) {} 618 } 619 620 /** 621 * Run some workloads with different object/value life times... 622 */ 623 public static void testActiveGc() { 624 try { 625 int nofThreads = 7; 626 int workSize = nofThreads * 10; 627 628 Object longLivedObjects = createLongLived(); 629 Object longLivedPeople = createPeople(); 630 Object longLivedPeopleVcc = createPeopleVcc(); 631 632 Object medLivedObjects = createLongLived(); 633 Object medLivedPeople = createPeople(); 634 Object medLivedPeopleVcc = createPeopleVcc(); 635 636 doGc(); 637 638 ForkJoinPool fjPool = new ForkJoinPool(nofThreads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); 639 640 // submit work until we see some GC 641 Reference ref = createRef(); 642 submitNewWork(fjPool, workSize); 643 while (ref.get() != null) { 644 if (fjPool.hasQueuedSubmissions()) { 645 sleepNoThrow(1L); 646 } 647 else { 648 workSize *= 2; // Grow the submission size 649 submitNewWork(fjPool, workSize); 650 } 651 } 652 653 // Keep working and actively GC, until MIN_ACTIVE_GC_COUNT 654 int nofActiveGc = 1; 655 ref = createRef(); 656 while (nofActiveGc < MIN_ACTIVE_GC_COUNT) { 657 if (ref.get() == null) { 658 nofActiveGc++; 659 ref = createRef(); 660 if (nofActiveGc % MED_ACTIVE_GC_COUNT == 0) { 661 validateLongLived(medLivedObjects); 662 validatePeople(medLivedPeople); 663 validatePeopleVcc(medLivedPeopleVcc); 664 665 medLivedObjects = createLongLived(); 666 medLivedPeople = createPeople(); 667 medLivedPeopleVcc = createPeopleVcc(); 668 } 669 } 670 else if (fjPool.hasQueuedSubmissions()) { 671 sleepNoThrow((long) Utils.getRandomInstance().nextInt(1000)); 672 doGc(); 673 } 674 else { 675 submitNewWork(fjPool, workSize); 676 } 677 } 678 fjPool.shutdown(); 679 680 validateLongLived(medLivedObjects); 681 validatePeople(medLivedPeople); 682 validatePeopleVcc(medLivedPeopleVcc); 683 medLivedObjects = null; 684 medLivedPeople = null; 685 medLivedPeopleVcc = null; 686 687 validateLongLived(longLivedObjects); 688 validatePeople(longLivedPeople); 689 validatePeopleVcc(longLivedPeopleVcc); 690 691 longLivedObjects = null; 692 longLivedPeople = null; 693 longLivedPeopleVcc = null; 694 695 doGc(); 696 } 697 catch (Throwable t) { fail("testMvtActiveGc", t); } 698 } 699 700 static final ReferenceQueue<Object> REFQ = new ReferenceQueue<>(); 701 702 public static void doGc() { 703 // Create Reference, wait until it clears... 704 Reference ref = createRef(); 705 while (ref.get() != null) { 706 System.gc(); 707 } 708 } 709 710 static Reference createRef() { 711 return new WeakReference<Object>(new Object(), REFQ); 712 } 713 714 static void validatePersonVcc(Object obj, int id, String fn, String ln, boolean equals) { 715 assertTrue(obj.getClass() == PersonVcc.class, "Expected VCC class"); 716 PersonVcc person = (PersonVcc) obj; 717 assertTrue(person.id == id); 718 if (equals) { 719 assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName"); 720 assertTrue(ln.equals(person.getLastName()), "Invalid field lastName"); 721 } 722 else { 723 assertTrue(person.getFirstName() == fn, "Invalid field firstName"); 724 assertTrue(person.getLastName() == ln, "Invalid field lastName"); 725 } 726 } 727 728 static PersonVcc createIndexedPersonVcc(int i) { 729 return PersonVcc.create(i, firstName(i), lastName(i)); 730 } 731 732 static void validateIndexedPersonVcc(Object obj, int i) { 733 validatePersonVcc(obj, i, firstName(i), lastName(i), true); 734 } 735 736 static PersonVcc createDefaultPersonVcc() { 737 return PersonVcc.create(0, null, null); 738 } 739 740 static void validateDefaultPersonVcc(Object obj) { 741 validatePersonVcc(obj, 0, null, null, false); 742 } 743 744 745 static void validatePerson(Person person, int id, String fn, String ln, boolean equals) { 746 assertTrue(person.id == id); 747 if (equals) { 748 assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName"); 749 assertTrue(ln.equals(person.getLastName()), "Invalid field lastName"); 750 } 751 else { 752 assertTrue(person.getFirstName() == fn, "Invalid field firstName"); 753 assertTrue(person.getLastName() == ln, "Invalid field lastName"); 754 } 755 } 756 757 static Person createIndexedPerson(int i) { 758 return Person.create(i, firstName(i), lastName(i)); 759 } 760 761 static void validateIndexedPerson(Person person, int i) { 762 validatePerson(person, i, firstName(i), lastName(i), true); 763 } 764 765 static Person createDefaultPerson() { 766 return Person.create(0, null, null); 767 } 768 769 static void validateDefaultPerson(Person person) { 770 validatePerson(person, 0, null, null, false); 771 } 772 773 static String firstName(int i) { 774 return "FirstName-" + i; 775 } 776 777 static String lastName(int i) { 778 return "LastName-" + i; 779 } 780 781 static Object createLongLived() throws Throwable { 782 Object[] population = new Object[2]; 783 population[0] = createPeople(); 784 population[1] = createPeopleVcc(); 785 return population; 786 } 787 788 static void validateLongLived(Object pop) throws Throwable { 789 Object[] population = (Object[]) pop; 790 validatePeople(population[0]); 791 validatePeopleVcc(population[1]); 792 } 793 794 static Object createPeopleVcc() throws Throwable { 795 int arrayLength = NOF_PEOPLE; 796 Class<?> vccClass = PersonVcc.class; 797 ValueType<?> vt = ValueType.forClass(vccClass); 798 MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(vt.arrayValueClass()); 799 800 Object people = vt.newArray().invoke(arrayLength); 801 for (int i = 0; i < arrayLength; i++) { 802 arrayElemSet.invoke(people, i, createIndexedPersonVcc(i)); 803 } 804 return people; 805 } 806 807 static void validatePeopleVcc(Object people) throws Throwable { 808 MethodHandle arrayElemGet = MethodHandles.arrayElementGetter( 809 ValueType.forClass((Class<?>)PersonVcc.class).arrayValueClass()); 810 811 int arrayLength = java.lang.reflect.Array.getLength(people); 812 assertTrue(arrayLength == NOF_PEOPLE); 813 for (int i = 0; i < arrayLength; i++) { 814 validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i); 815 } 816 } 817 818 static Object createPeople() { 819 int arrayLength = NOF_PEOPLE; 820 Person[] people = new Person[arrayLength]; 821 for (int i = 0; i < arrayLength; i++) { 822 people[i] = createIndexedPerson(i); 823 } 824 return people; 825 } 826 827 static void validatePeople(Object array) { 828 Person[] people = (Person[]) array; 829 int arrayLength = people.length; 830 assertTrue(arrayLength == NOF_PEOPLE); 831 for (int i = 0; i < arrayLength; i++) { 832 validateIndexedPerson(people[i], i); 833 } 834 } 835 836 // Various field layouts...sanity testing, see MVTCombo testing for full-set 837 838 static final __ByValue class ObjectValue { 839 final Object object; 840 841 private ObjectValue(Object object) { 842 this.object = object; 843 } 844 } 845 846 static class ObjectWithObjectValue { 847 ObjectValue value1; 848 Object ref1; 849 } 850 851 static class ObjectWithObjectValues { 852 ObjectValue value1; 853 ObjectValue value2; 854 Object ref1; 855 } 856 857 static class Foo { 858 int id; 859 String name; 860 String description; 861 long timestamp; 862 String notes; 863 } 864 865 static class Bar extends Foo { 866 long extendedId; 867 String moreNotes; 868 int count; 869 String otherStuff; 870 } 871 872 static final __ByValue class FooValue { 873 final int id; 874 final String name; 875 final String description; 876 final long timestamp; 877 final String notes; 878 879 private FooValue() { 880 this.id = 0; 881 this.name = null; 882 this.description = null; 883 this.timestamp = 0L; 884 this.notes = null; 885 } 886 887 __ValueFactory public static FooValue create(int id, String name, String description, long timestamp, String notes) { 888 FooValue f = __MakeDefault FooValue(); 889 f.id = id; 890 f.name = name; 891 f.description = description; 892 f.timestamp = timestamp; 893 f.notes = notes; 894 return f; 895 } 896 897 public static void testFrameOopsDefault(Object[][] oopMaps) { 898 Class<?> fooValueCls = FooValue.class; 899 MethodType mt = MethodType.methodType(Void.TYPE, oopMaps.getClass()); 900 int oopMapsSlot = 0; 901 int vtSlot = 1; 902 903 // Slots 1=oopMaps 904 // OopMap Q=RRR (.name .description .someNotes) 905 try { 906 MethodHandleBuilder 907 .loadCode(MethodHandles.lookup(), 908 "exerciseVBytecodeExprStackWithDefault", mt, 909 CODE->{ 910 CODE 911 .vdefault(fooValueCls) 912 .aload(oopMapsSlot) 913 .iconst_0() // Test-D0 Slots=R Stack=Q(RRR)RV 914 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 915 .aastore() 916 .pop() 917 .aload(oopMapsSlot) 918 .iconst_1() // Test-D1 Slots=R Stack=RV 919 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 920 .aastore() 921 .vdefault(fooValueCls) 922 .vstore(vtSlot) 923 .aload(oopMapsSlot) 924 .iconst_2() // Test-D2 Slots=RQ(RRR) Stack=RV 925 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 926 .aastore() 927 .vload(vtSlot) 928 .aconst_null() 929 .astore(vtSlot) // Storing null over the Q slot won't remove the ref, but should be single null ref 930 .aload(oopMapsSlot) 931 .iconst_3() // Test-D3 Slots=R Stack=Q(RRR)RV 932 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 933 .aastore() 934 .pop() 935 .return_(); 936 }).invoke(oopMaps); 937 } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); } 938 } 939 940 public static void testFrameOopsRefs(String name, String description, String notes, Object[][] oopMaps) { 941 Class<?> fooValueCls = FooValue.class; 942 FooValue f = create(4711, name, description, 9876543231L, notes); 943 FooValue[] fa = new FooValue[] { f }; 944 MethodType mt = MethodType.methodType(Void.TYPE, fa.getClass(), oopMaps.getClass()); 945 int fooArraySlot = 0; 946 int oopMapsSlot = 1; 947 try { 948 MethodHandleBuilder 949 .loadCode(MethodHandles.lookup(), 950 "exerciseVBytecodeExprStackWithRefs", mt, 951 CODE->{ 952 CODE 953 .aload(fooArraySlot) 954 .iconst_0() 955 .vaload() 956 .aload(oopMapsSlot) 957 .iconst_0() // Test-R0 Slots=RR Stack=Q(RRR)RV 958 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 959 .aastore() 960 .pop() 961 .return_(); 962 }).invoke(fa, oopMaps); 963 } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); } 964 } 965 } 966 967 static class BarWithValue { 968 FooValue foo; 969 long extendedId; 970 String moreNotes; 971 int count; 972 String otherStuff; 973 } 974 975 static final __ByValue class BarValue { 976 final FooValue foo; 977 final long extendedId; 978 final String moreNotes; 979 final int count; 980 final String otherStuff; 981 982 private BarValue(FooValue foo, long extendedId, String moreNotes, int count, String otherStuff) { 983 this.foo = foo; 984 this.extendedId = extendedId; 985 this.moreNotes = moreNotes; 986 this.count = count; 987 this.otherStuff = otherStuff; 988 } 989 } 990 991 } 992