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.MethodHandleBuilder;
  30 import jdk.incubator.mvt.ValueType;
  31 
  32 import static jdk.test.lib.Asserts.*;
  33 import jdk.test.lib.Utils;
  34 
  35 /**
  36  * @test ValueOopsMvt
  37  * @summary Test embedding oops into Minimal Value Types
  38  * @modules java.base/jdk.experimental.bytecode
  39  *          java.base/jdk.experimental.value
  40  *          jdk.incubator.mvt
  41  * @library /test/lib
  42  * @compile PersonVcc.java
  43  * @compile ValueOopsMvt.java
  44  * @run main/othervm -Xint -XX:+UseSerialGC -Xmx128m -XX:+EnableMVT
  45  *                   runtime.valhalla.valuetypes.ValueOopsMvt
  46  * @run main/othervm -Xint -XX:+UseG1GC -Xmx128m -XX:+EnableMVT
  47  *                   -XX:-ValueArrayFlatten
  48  *                   runtime.valhalla.valuetypes.ValueOopsMvt
  49  * @run main/othervm -Xint -XX:+UseG1GC -Xmx128m -XX:+EnableMVT
  50  *                   runtime.valhalla.valuetypes.ValueOopsMvt 100
  51  * @run main/othervm -Xint -XX:+UseParallelGC -Xmx128m -XX:+EnableMVT
  52  *                   runtime.valhalla.valuetypes.ValueOopsMvt
  53  * @run main/othervm -Xcomp -XX:+UseSerialGC -Xmx128m -XX:+EnableMVT
  54  *                   runtime.valhalla.valuetypes.ValueOopsMvt
  55  * @run main/othervm -Xcomp -XX:+UseG1GC -Xmx128m -XX:+EnableMVT
  56  *                   runtime.valhalla.valuetypes.ValueOopsMvt 100
  57  * @run main/othervm -Xcomp -XX:+UseParallelGC -Xmx128m -XX:+EnableMVT
  58  *                   runtime.valhalla.valuetypes.ValueOopsMvt
  59  */
  60 public class ValueOopsMvt {
  61 
  62     // Extra debug: -XX:+VerifyOops -XX:+VerifyStack -XX:+VerifyLastFrame -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:+VerifyDuringGC -XX:VerifySubSet=threads,heap
  63     // Even more debugging: -XX:+TraceNewOopMapGeneration -Xlog:gc*=info
  64 
  65 
  66     /*
  67      * TODO: Crashes with -Xcomp -XX:+UseG1GC -Xmx128m -XX:+EnableMVT -XX:-ValueArrayFlatten runtime.valhalla.valuetypes.ValueOopsMvt
  68      */
  69 
  70     static final int NOF_PEOPLE = 1000; // Exercise arrays of this size
  71 
  72     static int MIN_ACTIVE_GC_COUNT = 10; // Run active workload for this number of GC passes
  73 
  74     static int MED_ACTIVE_GC_COUNT = 4;  // Medium life span in terms of GC passes
  75 
  76     static final String TEST_STRING1 = "Test String 1";
  77     static final String TEST_STRING2 = "Test String 2";
  78 
  79     static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
  80 
  81     public static void main(String[] args) {
  82         if (args.length > 0) {
  83             MIN_ACTIVE_GC_COUNT = Integer.parseInt(args[0]);
  84         }
  85         testClassLoad();
  86         testBytecodes();
  87         testMvt();
  88 
  89         // Check we survive GC...
  90         testOverGc();   // Exercise root scan / oopMap
  91         testActiveGc(); // Brute force
  92     }
  93 
  94     /**
  95      * Test ClassFileParser can load values with reference fields
  96      */
  97     public static void testClassLoad() {
  98         // MVT
  99         Class<?> vccClass = PersonVcc.class;
 100         ValueType<?> vt = ValueType.forClass(vccClass);
 101         Class<?> boxClass = vt.boxClass();
 102         Class<?> dvtClass = vt.valueClass();
 103         Class<?> arrayClass = vt.arrayValueClass();
 104         dvtClass.toString();
 105     }
 106 
 107 
 108     /*
 109       Following tests are broken down into different use cases, and used in
 110       multi-threaded stress tests.
 111 
 112       Keeping the use cases separated helps isolate problems when debugging.
 113 
 114       Since we are testing the VM here, no the ValueType API is mostly ignored
 115       and the test exercises specific bytecode
 116      */
 117 
 118     /*
 119       Method Handle generation is mixed into the invokation code as an attempt
 120       to increase readability.
 121      */
 122 
 123     /**
 124      * Test Values with Oops with specific bytecodes for various use cases
 125      *
 126      * Value type specific bytecodes...
 127      *
 128      * vload
 129      * vstore
 130      * vaload
 131      * vastore
 132      * vreturn
 133      * vdefault
 134      * vwithfield
 135      * vbox
 136      * vunbox
 137      *
 138      * Bytecode accepting value types (QTypes)
 139      *
 140      * anewarray
 141      * multianewarray
 142      * getfield
 143      */
 144     public static void testBytecodes() {
 145         try {
 146             testBytecodesStackVDefault();
 147             testBytecodesStackVUnbox();
 148             testBytecodesStackAndSlots();
 149             testBytecodesStackAndSlotsDeep();
 150             testBytecodesVBox();
 151             testBytecodesVReturn();
 152             testBytecodesGetField();
 153             testBytecodesVwithfield();
 154             testBytecodesValueArray();
 155             testBytecodesField();
 156         } catch (Throwable t) {
 157             throw new RuntimeException(t);
 158         }
 159     }
 160 
 161     static MethodHandle stacksVDefaultTest;
 162 
 163     // vdefault on stack and pop
 164     public static void testBytecodesStackVDefault() throws Throwable {
 165         if (stacksVDefaultTest == null) { // Gen MH once
 166             stacksVDefaultTest = MethodHandleBuilder.loadCode(LOOKUP,
 167                 "stacksVDefaultTest", MethodType.methodType(Void.TYPE),
 168                 CODE->{
 169                 CODE
 170                 .vdefault(ValueType.forClass(PersonVcc.class).valueClass())
 171                 .pop()
 172                 .return_();
 173                 });
 174         }
 175         stacksVDefaultTest.invokeExact();
 176     }
 177 
 178     static MethodHandle stackVUnboxTest;
 179 
 180     // vunbox on stack and pop
 181     public static void testBytecodesStackVUnbox() throws Throwable {
 182         if (stackVUnboxTest == null) {
 183             stackVUnboxTest = MethodHandleBuilder.loadCode(LOOKUP,
 184                "stackVUnboxTest", MethodType.methodType(Void.TYPE, PersonVcc.class),
 185                 CODE->{
 186                 CODE
 187                 .aload(0)
 188                 .vunbox(ValueType.forClass(PersonVcc.class).valueClass())
 189                 .pop()
 190                 .return_();
 191                 });
 192         }
 193         stackVUnboxTest.invokeExact(createIndexedPersonVcc(7341));
 194     }
 195 
 196     static MethodHandle stackAndSlotsTest;
 197 
 198     // load/store with stack and slots
 199     public static void testBytecodesStackAndSlots() throws Throwable {
 200         if (stackAndSlotsTest == null) {
 201             stackAndSlotsTest = MethodHandleBuilder.loadCode(LOOKUP,
 202                "stackAndSlotsTest", MethodType.methodType(Void.TYPE, PersonVcc.class),
 203                 CODE->{
 204                 CODE
 205                 .aload(0)
 206                 .vunbox(ValueType.forClass(PersonVcc.class).valueClass())
 207                 .vstore(1)
 208                 .vload(1)
 209                 .vstore(2)
 210                 .return_();
 211                 });
 212         }
 213         stackAndSlotsTest.invokeExact(createIndexedPersonVcc(7342));
 214     }
 215 
 216     static MethodHandle stackAndSlotsDeepTest;
 217 
 218     // load/store with stack and slots, and call deeper
 219     public static void testBytecodesStackAndSlotsDeep() throws Throwable {
 220         if (stackAndSlotsDeepTest == null) {
 221             stackAndSlotsDeepTest = MethodHandleBuilder.loadCode(LOOKUP,
 222                "stackAndSlotsDeepTest", MethodType.methodType(Void.TYPE, PersonVcc.class),
 223                 CODE->{
 224                 CODE
 225                 .aload(0)
 226                 .vunbox(ValueType.forClass(PersonVcc.class).valueClass())
 227                 .vstore(1)
 228                 .vload(1)
 229                 .vstore(2)
 230                 .vload(2)
 231                 .invokestatic(ValueOopsMvt.class, "testBytecodesStackAndSlots", "()V", false)
 232                 .pop()
 233                 .return_();
 234                 });
 235         }
 236         stackAndSlotsDeepTest.invokeExact(createIndexedPersonVcc(7343));
 237     }
 238 
 239     static MethodHandle vboxTest;
 240 
 241     // vbox/vunbox, value on stack
 242     public static void testBytecodesVBox() throws Throwable {
 243         if (vboxTest == null) {
 244             vboxTest = MethodHandleBuilder.loadCode(LOOKUP,
 245                 "unboxBox", MethodType.methodType(PersonVcc.class, PersonVcc.class),
 246                 CODE->{
 247                 CODE
 248                 .aload(0)
 249                 .vunbox(ValueType.forClass(PersonVcc.class).valueClass())
 250                 .vbox(PersonVcc.class)
 251                 .areturn();
 252                 });
 253         }
 254         int index = 7344;
 255         PersonVcc person = (PersonVcc) vboxTest.invokeExact(createIndexedPersonVcc(index));
 256         validateIndexedPersonVcc(person, index);
 257     }
 258 
 259     static MethodHandle vreturnTest;
 260 
 261     // vreturn and pass value as arg
 262     public static void testBytecodesVReturn() throws Throwable {
 263         if (vreturnTest == null) {
 264             MethodHandle vunboxVReturn = MethodHandleBuilder.loadCode(LOOKUP,
 265                 "vunboxVReturn", MethodType.methodType(ValueType.forClass(PersonVcc.class).valueClass(), PersonVcc.class),
 266                 CODE->{
 267                 CODE
 268                 .aload(0)
 269                 .vunbox(ValueType.forClass(PersonVcc.class).valueClass())
 270                 .vreturn();
 271                 });
 272             MethodHandle vboxAReturn = MethodHandleBuilder.loadCode(LOOKUP,
 273                 "vboxAReturn", MethodType.methodType(PersonVcc.class, ValueType.forClass(PersonVcc.class).valueClass()),
 274                 CODE->{
 275                 CODE
 276                 .vload(0)
 277                 .vbox(PersonVcc.class)
 278                 .areturn();
 279                 });
 280             vreturnTest = MethodHandles.filterReturnValue(vunboxVReturn, vboxAReturn);
 281         }
 282         int index = 7345;
 283         PersonVcc person = (PersonVcc) vreturnTest.invokeExact(createIndexedPersonVcc(index));
 284         validateIndexedPersonVcc(person, index);
 285     }
 286 
 287     static MethodHandle getFieldTest;
 288 
 289     public static void testBytecodesGetField() throws Throwable {
 290         if (getFieldTest == null) {
 291             getFieldTest = MethodHandleBuilder.loadCode(LOOKUP,
 292                 "getFieldTest", MethodType.methodType(String.class, PersonVcc.class),
 293                 CODE->{
 294                 CODE
 295                 .aload(0)
 296                 .vunbox(ValueType.forClass(PersonVcc.class).valueClass())
 297                 .getfield(ValueType.forClass(PersonVcc.class).valueClass(), "lastName", "Ljava/lang/String;")
 298                 .areturn();
 299                 });
 300         }
 301         int index = 7346;
 302         String lastName = (String) getFieldTest.invokeExact(createIndexedPersonVcc(index));
 303         assertEquals(lastName, lastName(index));
 304     }
 305 
 306     static MethodHandle vwtihfieldTest;
 307 
 308     public static void testBytecodesVwithfield() throws Throwable {
 309         if (vwtihfieldTest == null) {
 310             Class<?> dvtClass = ValueType.forClass(PersonVcc.class).valueClass();
 311             vwtihfieldTest = MethodHandleBuilder.loadCode(MethodHandles.privateLookupIn(dvtClass, LOOKUP),
 312                 "vwtihfieldTest", MethodType.methodType(PersonVcc.class, PersonVcc.class, Integer.TYPE, String.class, String.class),
 313                 CODE->{
 314                 CODE
 315                 .aload(0)
 316                 .vunbox(dvtClass)
 317                 .iload(1)
 318                 .vwithfield(dvtClass, "id", "I")
 319                 .aload(2)
 320                 .vwithfield(dvtClass, "firstName", "Ljava/lang/String;")
 321                 .aload(3)
 322                 .vwithfield(dvtClass, "lastName", "Ljava/lang/String;")
 323                 .vbox(PersonVcc.class)
 324                 .areturn();
 325                 });
 326         }
 327         int index = 7347;
 328         int diffIndex = 4711;
 329         PersonVcc person = (PersonVcc) vwtihfieldTest.invokeExact(createIndexedPersonVcc(index), diffIndex, firstName(diffIndex), lastName(diffIndex));
 330         validateIndexedPersonVcc(person, diffIndex);
 331     }
 332 
 333     // load/store with arrays
 334     public static void testBytecodesValueArray() throws Throwable {
 335         testBytecodesArrayNew();
 336         testBytecodesArrayLoad();
 337         testBytecodesArrayStore();
 338         testBytecodesArrayStoreLoad();
 339 
 340         // multidim...
 341     }
 342 
 343     static MethodHandle anewarrayTest;
 344 
 345     public static void testBytecodesArrayNew() throws Throwable {
 346         if (anewarrayTest == null) {
 347             anewarrayTest = MethodHandleBuilder.loadCode(LOOKUP,
 348                 "anewarrayTest", MethodType.methodType(Integer.TYPE, Integer.TYPE),
 349                 CODE->{
 350                 CODE
 351                 .iload(0)
 352                 .anewarray(ValueType.forClass(PersonVcc.class).valueClass())
 353                 .arraylength()
 354                 .ireturn();
 355                 });
 356         }
 357         int asize = (int) anewarrayTest.invokeExact(NOF_PEOPLE);
 358         assertTrue(asize == NOF_PEOPLE, "Invariant");
 359     }
 360 
 361     static MethodHandle valoadTest;
 362 
 363     public static void testBytecodesArrayLoad() throws Throwable {
 364         if (valoadTest == null) {
 365             valoadTest = MethodHandleBuilder.loadCode(LOOKUP,
 366                 "valoadTest", MethodType.methodType(PersonVcc.class, Integer.TYPE, Integer.TYPE),
 367                 CODE->{
 368                 CODE
 369                 .iload(0)
 370                 .anewarray(ValueType.forClass(PersonVcc.class).valueClass())
 371                 .iload(1)
 372                 .vaload()
 373                 .vbox(PersonVcc.class)
 374                 .areturn();
 375                 });
 376         }
 377         PersonVcc person = (PersonVcc) valoadTest.invokeExact(NOF_PEOPLE, 7);
 378         validateDefaultPersonVcc(person);
 379     }
 380 
 381     static MethodHandle varrayStoreTest;
 382 
 383     public static void testBytecodesArrayStore() throws Throwable {
 384         if (varrayStoreTest == null) {
 385             varrayStoreTest = MethodHandleBuilder.loadCode(LOOKUP,
 386                 "varrayStoreTest", MethodType.methodType(Void.TYPE, Integer.TYPE, Integer.TYPE, PersonVcc.class),
 387                 CODE->{
 388                 CODE
 389                 .iload(0)
 390                 .anewarray(ValueType.forClass(PersonVcc.class).valueClass())
 391                 .iload(1)
 392                 .aload(2)
 393                 .vunbox(ValueType.forClass(PersonVcc.class).valueClass())
 394                 .vastore()
 395                 .return_();
 396                 });
 397         }
 398         int index = 11;
 399         varrayStoreTest.invokeExact(NOF_PEOPLE, index, createIndexedPersonVcc(index));
 400     }
 401 
 402     static MethodHandle varrayStoreLoadTest;
 403 
 404     public static void testBytecodesArrayStoreLoad() throws Throwable {
 405         if (varrayStoreLoadTest == null) {
 406             varrayStoreLoadTest = MethodHandleBuilder.loadCode(LOOKUP,
 407                 "varrayStoreLoadTest", MethodType.methodType(PersonVcc.class, Integer.TYPE, Integer.TYPE, PersonVcc.class),
 408                 CODE->{
 409                 CODE
 410                 .iload(0)
 411                 .anewarray(ValueType.forClass(PersonVcc.class).valueClass())
 412                 .dup()
 413                 .iload(1)
 414                 .aload(2)
 415                 .vunbox(ValueType.forClass(PersonVcc.class).valueClass())
 416                 .vastore()
 417                 .iload(1)
 418                 .vaload()
 419                 .vbox(PersonVcc.class)
 420                 .areturn();
 421                 });
 422         }
 423         int index = NOF_PEOPLE - 1;
 424         PersonVcc person = (PersonVcc) varrayStoreLoadTest.invokeExact(NOF_PEOPLE, index, createIndexedPersonVcc(index));
 425         validateIndexedPersonVcc(person, index);
 426     }
 427 
 428     // load/store with Object fields
 429     public static void testBytecodesField() throws Throwable {
 430         // CMH: no MVT support yet
 431     }
 432 
 433     /**
 434      * Check value type operations with "Minimal Value Types" (MVT) API
 435      */
 436     public static void testMvt() {
 437         try {
 438             // MVT...
 439             ValueType<?> vt = ValueType.forClass(PersonVcc.class);
 440             Class<?> vtClass = vt.valueClass();
 441             Class<?> arrayClass = vt.arrayValueClass();
 442 
 443             Object obj = vt.defaultValueConstant().invoke();
 444             validateDefaultPersonVcc(obj);
 445 
 446             obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box())
 447                 .invoke(createDefaultPersonVcc());
 448             validateDefaultPersonVcc(obj);
 449 
 450             int index = 11;
 451             obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box())
 452                 .invoke(createIndexedPersonVcc(index));
 453             validateIndexedPersonVcc(obj, index);
 454 
 455             testMvtArray("testMvt.array.1", 1);
 456         }
 457         catch (Throwable t) {
 458             fail("testMvtfailed", t);
 459         }
 460     }
 461 
 462     /**
 463      * MVT array operations...
 464      */
 465     public static void testMvtPeopleArray() {
 466         testMvtArray("testMvtPeopleArray", NOF_PEOPLE);
 467     }
 468 
 469     public static void testMvtArray(String testName, int arrayLength) {
 470         try {
 471             Class<?> vcc = PersonVcc.class;
 472             ValueType<?> vt = ValueType.forClass(vcc);
 473             Class<?> dvtClass = vt.valueClass();
 474             Class<?> arrayClass = vt.arrayValueClass();
 475 
 476             MethodHandle arrayElemGet = MethodHandles.arrayElementGetter(arrayClass);
 477             MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(arrayClass);
 478 
 479             MethodHandle getId = LOOKUP.findGetter(dvtClass, "id", Integer.TYPE);
 480             MethodHandle getFirstName = LOOKUP.findGetter(dvtClass, "firstName", String.class);
 481             MethodHandle getLastName = LOOKUP.findGetter(dvtClass, "lastName", String.class);
 482 
 483             MethodHandle getIdFromArray = MethodHandles.filterReturnValue(arrayElemGet, getId);
 484             MethodHandle getFnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getFirstName);
 485             MethodHandle getLnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getLastName);
 486 
 487             Object people = vt.newArray().invoke(arrayLength);
 488             for (int i = 0; i < arrayLength; i++) {
 489                 arrayElemSet.invoke(people, i, createIndexedPersonVcc(i));
 490             }
 491 
 492             for (int i = 0; i < arrayLength; i++) {
 493                 validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i);
 494 
 495                 int id = (int) getIdFromArray.invoke(people, i);
 496                 assertTrue(id == i, "Invalid field: Id, should be: " + i + " got " + id);
 497                 String fn = (String) getFnFromArray.invoke(people, i);
 498                 assertTrue(fn.equals(firstName(i)), "Invalid field: firstName");
 499                 String ln = (String) getLnFromArray.invoke(people, i);
 500                 assertTrue(ln.equals(lastName(i)), "Invalid field: lastName");
 501             }
 502         }
 503         catch (Throwable t) {
 504             fail(testName + " failed", t);
 505         }
 506     }
 507 
 508     /**
 509      * Check forcing GC for combination of VT on stack/LVT etc works
 510      */
 511     public static void testOverGc() {
 512         try {
 513             Class<?> vccClass = PersonVcc.class;
 514             ValueType<?> vt = ValueType.forClass(vccClass);
 515             Class<?> vtClass = vt.valueClass();
 516             Class<?> arrayClass = vt.arrayValueClass();
 517 
 518             doGc();
 519 
 520             // VT on stack and lvt, null refs, see if GC flies
 521             MethodHandle moveValueThroughStackAndLvt = MethodHandleBuilder.loadCode(
 522                 LOOKUP,
 523                 "gcOverPerson",
 524                 MethodType.methodType(vccClass, vccClass),
 525                 CODE->{
 526                 CODE
 527                 .aload(0)
 528                 .vunbox(vtClass)
 529                 .invokestatic(ValueOopsMvt.class, "doGc", "()V", false) // Stack
 530                 .vstore(0)
 531                 .invokestatic(ValueOopsMvt.class, "doGc", "()V", false) // LVT
 532                 .vload(0)
 533                 .iconst_1()  // push a litte further down
 534                 .invokestatic(ValueOopsMvt.class, "doGc", "()V", false) // Stack,LVT
 535                 .pop()
 536                 .vbox(vccClass)
 537                 .areturn();
 538             });
 539             Object obj = moveValueThroughStackAndLvt.invoke(createDefaultPersonVcc());
 540             validateDefaultPersonVcc(obj);
 541             doGc();
 542             obj = null;
 543             doGc();
 544 
 545             int index = 4711;
 546             obj = moveValueThroughStackAndLvt.invoke(createIndexedPersonVcc(index));
 547             validateIndexedPersonVcc(obj, index);
 548             doGc();
 549             obj = null;
 550             doGc();
 551         }
 552         catch (Throwable t) { fail("testOverGc", t); }
 553     }
 554 
 555     static void submitNewWork(ForkJoinPool fjPool, int size) {
 556         for (int i = 0; i < size; i++) {
 557             fjPool.execute(ValueOopsMvt::testMvtPeopleArray);
 558             for (int j = 0; j < 100; j++) {
 559                 // JDK-8186718 random crashes in interpreter vbox and vunbox (with G1)
 560                 // test needs refactoring to more specific use cases for debugging.
 561                 fjPool.execute(ValueOopsMvt::testBytecodes);
 562                 fjPool.execute(ValueOopsMvt::testMvt);
 563             }
 564         }
 565     }
 566 
 567     static void sleepNoThrow(long ms) {
 568         try {
 569             Thread.sleep(ms);
 570         }
 571         catch (Throwable t) {}
 572     }
 573 
 574     /**
 575      * Run some workloads with different object/value life times...
 576      */
 577     public static void testActiveGc() {
 578         try {
 579             int nofThreads = 7;
 580             int workSize = nofThreads * 10;
 581 
 582             Object longLivedObjects = createLongLived();
 583             Object longLivedPeopleVcc = createPeopleVcc();
 584 
 585             Object medLivedObjects = createLongLived();
 586             Object medLivedPeopleVcc = createPeopleVcc();
 587 
 588             doGc();
 589 
 590             ForkJoinPool fjPool = new ForkJoinPool(nofThreads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
 591 
 592             // submit work until we see some GC
 593             Reference ref = createRef();
 594             submitNewWork(fjPool, workSize);
 595             while (ref.get() != null) {
 596                 if (fjPool.hasQueuedSubmissions()) {
 597                     sleepNoThrow(1L);
 598                 }
 599                 else {
 600                     workSize *= 2; // Grow the submission size
 601                     submitNewWork(fjPool, workSize);
 602                 }
 603             }
 604 
 605             // Keep working and actively GC, until MIN_ACTIVE_GC_COUNT
 606             int nofActiveGc = 1;
 607             ref = createRef();
 608             while (nofActiveGc < MIN_ACTIVE_GC_COUNT) {
 609                 if (ref.get() == null) {
 610                     nofActiveGc++;
 611                     ref = createRef();
 612                     if (nofActiveGc % MED_ACTIVE_GC_COUNT == 0) {
 613                         validateLongLived(medLivedObjects);
 614                         validatePeopleVcc(medLivedPeopleVcc);
 615 
 616                         medLivedObjects = createLongLived();
 617                         medLivedPeopleVcc = createPeopleVcc();
 618                     }
 619                 }
 620                 else if (fjPool.hasQueuedSubmissions()) {
 621                     sleepNoThrow((long) Utils.getRandomInstance().nextInt(1000));
 622                     doGc();
 623                 }
 624                 else {
 625                     submitNewWork(fjPool, workSize);
 626                 }
 627             }
 628             fjPool.shutdown();
 629 
 630             validateLongLived(medLivedObjects);
 631             validatePeopleVcc(medLivedPeopleVcc);
 632             medLivedObjects = null;
 633             medLivedPeopleVcc = null;
 634 
 635             validateLongLived(longLivedObjects);
 636             validatePeopleVcc(longLivedPeopleVcc);
 637 
 638             longLivedObjects = null;
 639             longLivedPeopleVcc = null;
 640 
 641             doGc();
 642         }
 643         catch (Throwable t) { fail("testMvtActiveGc", t); }
 644     }
 645 
 646     static final ReferenceQueue<Object> REFQ = new ReferenceQueue<>();
 647 
 648     public static void doGc() {
 649         // Create Reference, wait until it clears...
 650         Reference ref = createRef();
 651         while (ref.get() != null) {
 652             System.gc();
 653         }
 654     }
 655 
 656     static Reference createRef() {
 657         return new WeakReference<Object>(new Object(), REFQ);
 658     }
 659 
 660     static void validatePersonVcc(Object obj, int id, String fn, String ln, boolean equals) {
 661         assertTrue(obj.getClass() == PersonVcc.class, "Expected VCC class");
 662         PersonVcc person = (PersonVcc) obj;
 663         assertTrue(person.id == id);
 664         if (equals) {
 665             assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName");
 666             assertTrue(ln.equals(person.getLastName()), "Invalid  field lastName");
 667         }
 668         else {
 669             assertTrue(person.getFirstName() == fn, "Invalid field firstName");
 670             assertTrue(person.getLastName() == ln, "Invalid  field lastName");
 671         }
 672     }
 673 
 674     static PersonVcc createIndexedPersonVcc(int i) {
 675         return PersonVcc.create(i, firstName(i), lastName(i));
 676     }
 677 
 678     static void validateIndexedPersonVcc(Object obj, int i) {
 679         validatePersonVcc(obj, i, firstName(i), lastName(i), true);
 680     }
 681 
 682     static PersonVcc createDefaultPersonVcc() {
 683         return PersonVcc.create(0, null, null);
 684     }
 685 
 686     static void validateDefaultPersonVcc(Object obj) {
 687         validatePersonVcc(obj, 0, null, null, false);
 688     }
 689 
 690     static String firstName(int i) {
 691         return "FirstName-" + i;
 692     }
 693 
 694     static String lastName(int i) {
 695         return "LastName-" + i;
 696     }
 697 
 698     static Object createLongLived()  throws Throwable {
 699         Object[] population = new Object[1];
 700         population[0] = createPeopleVcc();
 701         return population;
 702     }
 703 
 704     static void validateLongLived(Object pop) throws Throwable {
 705         Object[] population = (Object[]) pop;
 706         validatePeopleVcc(population[0]);
 707     }
 708 
 709     static Object createPeopleVcc() throws Throwable {
 710         int arrayLength = NOF_PEOPLE;
 711         Class<?> vccClass = PersonVcc.class;
 712         ValueType<?> vt = ValueType.forClass(vccClass);
 713         MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(vt.arrayValueClass());
 714 
 715         Object people = vt.newArray().invoke(arrayLength);
 716         for (int i = 0; i < arrayLength; i++) {
 717             arrayElemSet.invoke(people, i, createIndexedPersonVcc(i));
 718         }
 719         return people;
 720     }
 721 
 722     static void validatePeopleVcc(Object people) throws Throwable {
 723         MethodHandle arrayElemGet = MethodHandles.arrayElementGetter(
 724             ValueType.forClass((Class<?>)PersonVcc.class).arrayValueClass());
 725 
 726         int arrayLength = java.lang.reflect.Array.getLength(people);
 727         assertTrue(arrayLength == NOF_PEOPLE);
 728         for (int i = 0; i < arrayLength; i++) {
 729             validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i);
 730         }
 731     }
 732 
 733 }
 734