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