Path: blob/master/test/hotspot/jtreg/vmTestbase/gc/hashcode/ExternalHashingTest/ExternalHashingTest.java
40948 views
/*1* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/22232425/*26* @test27* @key stress randomness28*29* @summary converted from VM Testbase gc/hashcode/ExternalHashingTest.30* VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, jrockit]31* VM Testbase readme:32* DESCRIPTION33* Test the possible interaction of external hashing and locking on object34* headers.35* The approach is to nearly simultaneously lock/hash a relatively small group36* of objects. We do this repeatedly (munging), recording all hash values37* collected therein.38* After doing this for a large number of groups, we force a garbage collection,39* which would change the hashCode of an object if it hasn't previously been40* hashed. In our case, we _know_ what the previous hashcode was, so we can41* recalculate all of their hashes and compare with the original value.42* If any of the hashCodes hash changed, we know we have a problem.43*44* COMMENTS45* This test was ported from JRockit test suite.46*47* @library /vmTestbase48* /test/lib49* @run main/othervm -XX:-UseGCOverheadLimit gc.hashcode.ExternalHashingTest.ExternalHashingTest50*/5152package gc.hashcode.ExternalHashingTest;5354import java.text.SimpleDateFormat;55import java.util.Date;56import java.util.Random;57import java.util.Vector;5859import jdk.test.lib.Utils;6061/**62* Test the possible interaction of external hashing and locking on object63* headers.64*65* The approach is to nearly simultaneously lock/hash a relatively small group66* of objects. We do this repeatedly (munging), recording all hash values67* collected therein.68*69* After doing this for a large number of groups, we force a garbage collection,70* which would change the hashCode of an object if it hasn't previously been71* hashed. In our case, we _know_ what the previous hashcode was, so we can72* recalculate all of their hashes and compare with the original value.73*74* If any of the hashCodes hash changed, we know we have a problem.75*/7677public final class ExternalHashingTest {7879/** Random number generator. */80static Random rand = Utils.getRandomInstance();8182/** Goes to true when the threads should start working. */83public static volatile boolean startingGun;8485/** Goes to true when the hashing thread is done. */86public static volatile boolean finishHashing;8788/** Goes to true when the locking thread is done. */89public static volatile boolean finishLocking;9091/** The number of objects in each batch. */92private static final int BATCH_SIZE = 20;9394/** This is the global list of objects that have been hashed. */95static Vector allObjects = new Vector();9697/** This is the corresponding list of hashCodes collected. */98static Vector allHashes = new Vector();99100/** The default milliseconds to run the program. */101private static final long DEFAULT_DURATION = 10000;102103/** All static */104private ExternalHashingTest() {}105106/**107* This object holds garbage. It is a (probably unnecessary){ embellishment108* to increase the amount of garbage created by this benchmark.109* <p>110* It is global to discourage optimizer from removing it.111*/112public static Object[] garbageMonger;113114/**115* We need a fairly short pause, since we're not using a semaphore to116* synchronize threads.117*/118public static void pause() {119try {120// Thread.sleep(100);121Thread.yield();122} catch (Exception e) {123e.printStackTrace();124System.exit(1);125}126}127128/**129* Returns System.currentTimeMillis() in the Date format.130* @return String131*/132private static String getDateString() {133SimpleDateFormat df = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss z");134Date date = new Date();135date.setTime(System.currentTimeMillis());136return df.format(date);137}138139/**140* Main driver.141* @param args command line arguments aren't used.142*/143public static void main(String[] args) {144145long timeToRun = DEFAULT_DURATION;;146147try {148for (int i = 0; i < args.length; i++) {149if ("-stressTime".equals(args[i])) {150if (i + 1 == args.length) {151throw new RuntimeException("Test bug: value of -stressTime option absents");152}153timeToRun = Long.parseLong(args[i + 1]);154if (timeToRun <= 0) {155throw new RuntimeException("Test bug: value of -stressTime option is not a positive number");156}157break;158}159}160} catch (NumberFormatException e) {161throw new RuntimeException("Test bug: Exception occured while parsing -stressTime option's value", e);162}163164long startTime = System.currentTimeMillis();165166System.out.println("[" + getDateString() + "] Test duration is: " + timeToRun + " ms");167System.out.println("[" + getDateString() + "] Do munge objects...");168while ((System.currentTimeMillis() - startTime) < timeToRun) {169for (int i = 0; i < 100; i++) {170mungeObjects();171}172System.out.println("[" + getDateString() + "] The next 100 objects are munged...");173}174175// Force a GC (so that objects move their position)176System.out.println("[" + getDateString() + "] Force a GC...");177garbageMonger = null;178System.gc();179180// Now, to check to see if hashes are correct181System.out.println("[" + getDateString() + "] Check hash codes...");182for (int i = 0; i < allObjects.size(); i++) {183Object o = allObjects.elementAt(i);184int hash = ((Integer) allHashes.elementAt(i)).intValue();185186if (o.hashCode() != hash) {187System.out.println("Inconsistent hash code found (Object "188+ i + " out of " + allObjects.size());189System.out.println("Object = " + o.toString() + "; hashCode = 0x"190+ Integer.toHexString(o.hashCode())191+ "; expected = 0x" + Integer.toHexString(hash));192System.exit(1);193}194}195196System.exit(95 /* PASSED */);197}198199/**200* Add a single batch of objects to the mix.201* <p>202* It prepares a list of candidate objects, and presents them to a203* LockerThread and a HasherThread in randomized orders.204* <p>205* The two threads are launched, and control is returned to the caller after206* they have finished their processing.207*/208private static void mungeObjects() {209210startingGun = false;211finishHashing = false;212finishLocking = false;213214/* Create the list of victims. */215Object[] candidates = new Object[BATCH_SIZE];216for (int i = 0; i < candidates.length; i++) {217candidates[i] = new Object();218}219220Object[] lockedList = randomize(candidates);221Object[] hashedList = randomize(candidates);222int[] foundHashes = new int[BATCH_SIZE];223224// Launch the child threads225LockerThread locker = new LockerThread(lockedList);226Thread lockerThread = new Thread(locker);227Thread hasherThread = new Thread(new HasherThread(hashedList,228foundHashes));229lockerThread.start();230hasherThread.start();231startingGun = true;232233while (!finishLocking || !finishHashing) {234pause();235}236237garbageMonger = new Object[BATCH_SIZE];238for (int i = 0; i < BATCH_SIZE; i++) {239/* Add all of the results of this pass to the global list. */240allObjects.add(hashedList[i]);241allHashes.add(Integer.valueOf(foundHashes[i]));242243/* Create even more garbage for the GC to find */244garbageMonger[i] = new Object();245}246247// just some noise to make sure that do-nothing code is not optimized248// away.249if (locker.getCount() != BATCH_SIZE) {250throw new InternalError("should not get here");251}252}253254/**255* Return the list of objects in random order256*/257private static Object[] randomize(Object[] list) {258259Vector v = new Vector();260for (int i = 0; i < list.length; i++) {261v.add(list[i]);262}263264Object[] result = new Object[list.length];265for (int i = 0; i < list.length; i++) {266int pos = rand.nextInt(list.length - i);267result[i] = v.remove(pos);268}269return result;270}271}272273/**274* This helper thread locks all objects in a list in a given order before275* returning.276*/277278class LockerThread implements Runnable {279280/** The list of objects to be locked. */281Object[] theList;282283/**284* This junk counter is an attempt to cause the contents of the synchronized285* block not to be completely optimized away.286*/287int count;288289/**290* Construct a LockerThread and provide a list of objects to work with.291* @param list the objects to lock.292*/293LockerThread(Object[] list) {294theList = list;295count = 0;296}297298/**299* Proceed to lock the objects...300*/301public void run() {302// Synchronize. Wait for caller to say all is go.303while (!ExternalHashingTest.startingGun) {304ExternalHashingTest.pause();305}306307// Perform the locking308for (int i = 0; i < theList.length; i++) {309synchronized (theList[i]) {310count++;311}312}313314// Tell the caller we are done.315ExternalHashingTest.finishLocking = true;316}317318/**319* Discourage compiler from removing do-nothing count field.320* @return the number of objects locked.321*/322public int getCount() {323return count;324}325}326327/**328* This helper thread hashes all objects in a list in a given order before329* returning.330*/331332class HasherThread implements Runnable {333334/** The list of objects to be hashed. */335Object[] theList;336337/** The list of hash codes. */338int[] theHashes;339340/**341* Construct a HasherThread and provide a list of objects to work with.342* @param list the objects to hash.343* @param hashes for storing the hash values.344*/345HasherThread(Object[] list, int[] hashes) {346theList = list;347theHashes = hashes;348}349350/**351* Proceed to hash the objects.352*/353public void run() {354// Synchronize. Wait for caller to say all is go.355while (!ExternalHashingTest.startingGun) {356ExternalHashingTest.pause();357}358359// Perform the hashing (collect for the caller)360for (int i = 0; i < theList.length; i++) {361theHashes[i] = theList[i].hashCode();362}363// Tell the caller we are done.364ExternalHashingTest.finishHashing = true;365}366}367368369