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