1 /* 2 * Copyright (c) 2008, 2018, 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 25 26 /* 27 * @test 28 * @key stress gc 29 * 30 * @summary converted from VM Testbase gc/hashcode/ExternalHashingTest. 31 * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, jrockit] 32 * VM Testbase readme: 33 * DESCRIPTION 34 * Test the possible interaction of external hashing and locking on object 35 * headers. 36 * The approach is to nearly simultaneously lock/hash a relatively small group 37 * of objects. We do this repeatedly (munging), recording all hash values 38 * collected therein. 39 * After doing this for a large number of groups, we force a garbage collection, 40 * which would change the hashCode of an object if it hasn't previously been 41 * hashed. In our case, we _know_ what the previous hashcode was, so we can 42 * recalculate all of their hashes and compare with the original value. 43 * If any of the hashCodes hash changed, we know we have a problem. 44 * 45 * COMMENTS 46 * This test was ported from JRockit test suite. 47 * 48 * @library /vmTestbase 49 * /test/lib 50 * @run driver jdk.test.lib.FileInstaller . . 51 * @run main/othervm -XX:-UseGCOverheadLimit gc.hashcode.ExternalHashingTest.ExternalHashingTest 52 */ 53 54 package gc.hashcode.ExternalHashingTest; 55 56 import java.text.SimpleDateFormat; 57 import java.util.Date; 58 import java.util.Random; 59 import java.util.Vector; 60 61 /** 62 * Test the possible interaction of external hashing and locking on object 63 * headers. 64 * 65 * The approach is to nearly simultaneously lock/hash a relatively small group 66 * of objects. We do this repeatedly (munging), recording all hash values 67 * 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 been 71 * hashed. In our case, we _know_ what the previous hashcode was, so we can 72 * 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 */ 76 77 public final class ExternalHashingTest { 78 79 /** Random number generator. */ 80 static Random rand = new Random(); 81 82 /** Goes to true when the threads should start working. */ 83 public static volatile boolean startingGun; 84 85 /** Goes to true when the hashing thread is done. */ 86 public static volatile boolean finishHashing; 87 88 /** Goes to true when the locking thread is done. */ 89 public static volatile boolean finishLocking; 90 91 /** The number of objects in each batch. */ 92 private static final int BATCH_SIZE = 20; 93 94 /** This is the global list of objects that have been hashed. */ 95 static Vector allObjects = new Vector(); 96 97 /** This is the corresponding list of hashCodes collected. */ 98 static Vector allHashes = new Vector(); 99 100 /** The default milliseconds to run the program. */ 101 private static final long DEFAULT_DURATION = 10000; 102 103 /** All static */ 104 private ExternalHashingTest() {} 105 106 /** 107 * This object holds garbage. It is a (probably unnecessary){ embellishment 108 * to increase the amount of garbage created by this benchmark. 109 * <p> 110 * It is global to discourage optimizer from removing it. 111 */ 112 public static Object[] garbageMonger; 113 114 /** 115 * We need a fairly short pause, since we're not using a semaphore to 116 * synchronize threads. 117 */ 118 public static void pause() { 119 try { 120 // Thread.sleep(100); 121 Thread.yield(); 122 } catch (Exception e) { 123 e.printStackTrace(); 124 System.exit(1); 125 } 126 } 127 128 /** 129 * Returns System.currentTimeMillis() in the Date format. 130 * @return String 131 */ 132 private static String getDateString() { 133 SimpleDateFormat df = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss z"); 134 Date date = new Date(); 135 date.setTime(System.currentTimeMillis()); 136 return df.format(date); 137 } 138 139 /** 140 * Main driver. 141 * @param args command line arguments aren't used. 142 */ 143 public static void main(String[] args) { 144 145 long timeToRun = DEFAULT_DURATION;; 146 147 try { 148 for (int i = 0; i < args.length; i++) { 149 if ("-stressTime".equals(args[i])) { 150 if (i + 1 == args.length) { 151 throw new RuntimeException("Test bug: value of -stressTime option absents"); 152 } 153 timeToRun = Long.parseLong(args[i + 1]); 154 if (timeToRun <= 0) { 155 throw new RuntimeException("Test bug: value of -stressTime option is not a positive number"); 156 } 157 break; 158 } 159 } 160 } catch (NumberFormatException e) { 161 throw new RuntimeException("Test bug: Exception occured while parsing -stressTime option's value", e); 162 } 163 164 long startTime = System.currentTimeMillis(); 165 166 System.out.println("[" + getDateString() + "] Test duration is: " + timeToRun + " ms"); 167 System.out.println("[" + getDateString() + "] Do munge objects..."); 168 while ((System.currentTimeMillis() - startTime) < timeToRun) { 169 for (int i = 0; i < 100; i++) { 170 mungeObjects(); 171 } 172 System.out.println("[" + getDateString() + "] The next 100 objects are munged..."); 173 } 174 175 // Force a GC (so that objects move their position) 176 System.out.println("[" + getDateString() + "] Force a GC..."); 177 garbageMonger = null; 178 System.gc(); 179 180 // Now, to check to see if hashes are correct 181 System.out.println("[" + getDateString() + "] Check hash codes..."); 182 for (int i = 0; i < allObjects.size(); i++) { 183 Object o = allObjects.elementAt(i); 184 int hash = ((Integer) allHashes.elementAt(i)).intValue(); 185 186 if (o.hashCode() != hash) { 187 System.out.println("Inconsistent hash code found (Object " 188 + i + " out of " + allObjects.size()); 189 System.out.println("Object = " + o.toString() + "; hashCode = 0x" 190 + Integer.toHexString(o.hashCode()) 191 + "; expected = 0x" + Integer.toHexString(hash)); 192 System.exit(1); 193 } 194 } 195 196 System.exit(95 /* PASSED */); 197 } 198 199 /** 200 * Add a single batch of objects to the mix. 201 * <p> 202 * It prepares a list of candidate objects, and presents them to a 203 * LockerThread and a HasherThread in randomized orders. 204 * <p> 205 * The two threads are launched, and control is returned to the caller after 206 * they have finished their processing. 207 */ 208 private static void mungeObjects() { 209 210 startingGun = false; 211 finishHashing = false; 212 finishLocking = false; 213 214 /* Create the list of victims. */ 215 Object[] candidates = new Object[BATCH_SIZE]; 216 for (int i = 0; i < candidates.length; i++) { 217 candidates[i] = new Object(); 218 } 219 220 Object[] lockedList = randomize(candidates); 221 Object[] hashedList = randomize(candidates); 222 int[] foundHashes = new int[BATCH_SIZE]; 223 224 // Launch the child threads 225 LockerThread locker = new LockerThread(lockedList); 226 Thread lockerThread = new Thread(locker); 227 Thread hasherThread = new Thread(new HasherThread(hashedList, 228 foundHashes)); 229 lockerThread.start(); 230 hasherThread.start(); 231 startingGun = true; 232 233 while (!finishLocking || !finishHashing) { 234 pause(); 235 } 236 237 garbageMonger = new Object[BATCH_SIZE]; 238 for (int i = 0; i < BATCH_SIZE; i++) { 239 /* Add all of the results of this pass to the global list. */ 240 allObjects.add(hashedList[i]); 241 allHashes.add(new Integer(foundHashes[i])); 242 243 /* Create even more garbage for the GC to find */ 244 garbageMonger[i] = new Object(); 245 } 246 247 // just some noise to make sure that do-nothing code is not optimized 248 // away. 249 if (locker.getCount() != BATCH_SIZE) { 250 throw new InternalError("should not get here"); 251 } 252 } 253 254 /** 255 * Return the list of objects in random order 256 */ 257 private static Object[] randomize(Object[] list) { 258 259 Vector v = new Vector(); 260 for (int i = 0; i < list.length; i++) { 261 v.add(list[i]); 262 } 263 264 Object[] result = new Object[list.length]; 265 for (int i = 0; i < list.length; i++) { 266 int pos = rand.nextInt(list.length - i); 267 result[i] = v.remove(pos); 268 } 269 return result; 270 } 271 } 272 273 /** 274 * This helper thread locks all objects in a list in a given order before 275 * returning. 276 */ 277 278 class LockerThread implements Runnable { 279 280 /** The list of objects to be locked. */ 281 Object[] theList; 282 283 /** 284 * This junk counter is an attempt to cause the contents of the synchronized 285 * block not to be completely optimized away. 286 */ 287 int count; 288 289 /** 290 * Construct a LockerThread and provide a list of objects to work with. 291 * @param list the objects to lock. 292 */ 293 LockerThread(Object[] list) { 294 theList = list; 295 count = 0; 296 } 297 298 /** 299 * Proceed to lock the objects... 300 */ 301 public void run() { 302 // Synchronize. Wait for caller to say all is go. 303 while (!ExternalHashingTest.startingGun) { 304 ExternalHashingTest.pause(); 305 } 306 307 // Perform the locking 308 for (int i = 0; i < theList.length; i++) { 309 synchronized (theList[i]) { 310 count++; 311 } 312 } 313 314 // Tell the caller we are done. 315 ExternalHashingTest.finishLocking = true; 316 } 317 318 /** 319 * Discourage compiler from removing do-nothing count field. 320 * @return the number of objects locked. 321 */ 322 public int getCount() { 323 return count; 324 } 325 } 326 327 /** 328 * This helper thread hashes all objects in a list in a given order before 329 * returning. 330 */ 331 332 class HasherThread implements Runnable { 333 334 /** The list of objects to be hashed. */ 335 Object[] theList; 336 337 /** The list of hash codes. */ 338 int[] theHashes; 339 340 /** 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 */ 345 HasherThread(Object[] list, int[] hashes) { 346 theList = list; 347 theHashes = hashes; 348 } 349 350 /** 351 * Proceed to hash the objects. 352 */ 353 public void run() { 354 // Synchronize. Wait for caller to say all is go. 355 while (!ExternalHashingTest.startingGun) { 356 ExternalHashingTest.pause(); 357 } 358 359 // Perform the hashing (collect for the caller) 360 for (int i = 0; i < theList.length; i++) { 361 theHashes[i] = theList[i].hashCode(); 362 } 363 // Tell the caller we are done. 364 ExternalHashingTest.finishHashing = true; 365 } 366 }