1 /*
   2  * Copyright (c) 2019, 2020, 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 gc.stress.gclocker;
  25 
  26 /*
  27  * @test TestExcessGCLockerCollections
  28  * @bug 8048556
  29  * @summary Check for GC Locker initiated GCs that immediately follow another
  30  * GC and so have very little needing to be collected.
  31  * @requires vm.gc != "Z"
  32  * @requires vm.gc != "Epsilon"
  33  * @requires vm.gc != "Shenandoah"
  34  * @library /test/lib
  35  * @modules java.base/jdk.internal.misc
  36  * @run driver/timeout=1000 gc.stress.gclocker.TestExcessGCLockerCollections 300 4 2
  37  */
  38 
  39 import java.util.HashMap;
  40 import java.util.Map;
  41 
  42 import java.util.zip.Deflater;
  43 
  44 import java.util.ArrayList;
  45 import java.util.Arrays;
  46 
  47 import jdk.test.lib.Asserts;
  48 import jdk.test.lib.process.ProcessTools;
  49 import jdk.test.lib.process.OutputAnalyzer;
  50 
  51 class TestExcessGCLockerCollectionsAux {
  52     static private final int LARGE_MAP_SIZE = 64 * 1024;
  53 
  54     static private final int MAP_ARRAY_LENGTH = 4;
  55     static private final int MAP_SIZE = 1024;
  56 
  57     static private final int BYTE_ARRAY_LENGTH = 128 * 1024;
  58 
  59     static private void println(String str) { System.out.println(str); }
  60 
  61     static private volatile boolean keepRunning = true;
  62 
  63     static Map<Integer,String> populateMap(int size) {
  64         Map<Integer,String> map = new HashMap<Integer,String>();
  65         for (int i = 0; i < size; i += 1) {
  66             Integer keyInt = Integer.valueOf(i);
  67             String valStr = "value is [" + i + "]";
  68             map.put(keyInt,valStr);
  69         }
  70         return map;
  71     }
  72 
  73     static private class AllocatingWorker implements Runnable {
  74         private final Object[] array = new Object[MAP_ARRAY_LENGTH];
  75         private int arrayIndex = 0;
  76 
  77         private void doStep() {
  78             Map<Integer,String> map = populateMap(MAP_SIZE);
  79             array[arrayIndex] = map;
  80             arrayIndex = (arrayIndex + 1) % MAP_ARRAY_LENGTH;
  81         }
  82 
  83         public void run() {
  84             while (keepRunning) {
  85                 doStep();
  86             }
  87         }
  88     }
  89 
  90     static private class JNICriticalWorker implements Runnable {
  91         private int count;
  92 
  93         private void doStep() {
  94             byte[] inputArray = new byte[BYTE_ARRAY_LENGTH];
  95             for (int i = 0; i < inputArray.length; i += 1) {
  96                 inputArray[i] = (byte) (count + i);
  97             }
  98 
  99             Deflater deflater = new Deflater();
 100             deflater.setInput(inputArray);
 101             deflater.finish();
 102 
 103             byte[] outputArray = new byte[2 * inputArray.length];
 104             deflater.deflate(outputArray);
 105 
 106             count += 1;
 107         }
 108 
 109         public void run() {
 110             while (keepRunning) {
 111                 doStep();
 112             }
 113         }
 114     }
 115 
 116     static public Map<Integer,String> largeMap;
 117 
 118     static public void main(String args[]) {
 119         long durationSec = Long.parseLong(args[0]);
 120         int allocThreadNum = Integer.parseInt(args[1]);
 121         int jniCriticalThreadNum = Integer.parseInt(args[2]);
 122 
 123         println("Running for " + durationSec + " secs");
 124 
 125         largeMap = populateMap(LARGE_MAP_SIZE);
 126 
 127         println("Starting " + allocThreadNum + " allocating threads");
 128         for (int i = 0; i < allocThreadNum; i += 1) {
 129             new Thread(new AllocatingWorker()).start();
 130         }
 131 
 132         println("Starting " + jniCriticalThreadNum + " jni critical threads");
 133         for (int i = 0; i < jniCriticalThreadNum; i += 1) {
 134             new Thread(new JNICriticalWorker()).start();
 135         }
 136 
 137         long durationMS = (long) (1000 * durationSec);
 138         long start = System.currentTimeMillis();
 139         long now = start;
 140         long soFar = now - start;
 141         while (soFar < durationMS) {
 142             try {
 143                 Thread.sleep(durationMS - soFar);
 144             } catch (Exception e) {
 145             }
 146             now = System.currentTimeMillis();
 147             soFar = now - start;
 148         }
 149         println("Done.");
 150         keepRunning = false;
 151     }
 152 }
 153 
 154 public class TestExcessGCLockerCollections {
 155     private static final String locker =
 156         "\\[gc\\s*\\] .* \\(GCLocker Initiated GC\\)";
 157     private static final String ANY_LOCKER = locker + " [1-9][0-9]*M";
 158     private static final String BAD_LOCKER = locker + " [1-9][0-9]?M";
 159 
 160     private static final String[] COMMON_OPTIONS = new String[] {
 161         "-Xmx1G", "-Xms1G", "-Xmn256M", "-Xlog:gc" };
 162 
 163     public static void main(String args[]) throws Exception {
 164         if (args.length < 3) {
 165             System.out.println("usage: TestExcessGCLockerCollectionsAux" +
 166                                " <duration sec> <alloc threads>" +
 167                                " <jni critical threads>");
 168             throw new RuntimeException("Invalid arguments");
 169         }
 170 
 171         ArrayList<String> finalArgs = new ArrayList<String>();
 172         finalArgs.addAll(Arrays.asList(COMMON_OPTIONS));
 173         finalArgs.add(TestExcessGCLockerCollectionsAux.class.getName());
 174         finalArgs.addAll(Arrays.asList(args));
 175 
 176         // GC and other options obtained from test framework.
 177         OutputAnalyzer output = ProcessTools.executeTestJvm(finalArgs);
 178         output.shouldHaveExitValue(0);
 179         //System.out.println("------------- begin stdout ----------------");
 180         //System.out.println(output.getStdout());
 181         //System.out.println("------------- end stdout ----------------");
 182         output.stdoutShouldMatch(ANY_LOCKER);
 183         output.stdoutShouldNotMatch(BAD_LOCKER);
 184     }
 185 }