1 /*
   2  * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2017, SAP SE and/or its affiliates. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 package gc.stress.TestJNIBlockFullGC;
  26 
  27 /*
  28  * @test TestJNIBlockFullGC
  29  * @summary Check that in G1 a Full GC to reclaim space can not be blocked out by the GC locker.
  30  * @key gc randomness
  31  * @requires vm.gc.G1
  32  * @library /test/lib
  33  * @run main/othervm/native -Xmx64m -XX:+UseG1GC -Xlog:gc=info,gc+alloc=trace -XX:MaxGCPauseMillis=10 gc.stress.TestJNIBlockFullGC.TestJNIBlockFullGC 10 10000 10000 10000 30000 10000 0.7
  34  */
  35 
  36 import java.lang.ref.SoftReference;
  37 import java.util.Random;
  38 import jdk.test.lib.Utils;
  39 
  40 public class TestJNIBlockFullGC {
  41     private static final Random rng = Utils.getRandomInstance();
  42 
  43     static {
  44         System.loadLibrary("TestJNIBlockFullGC");
  45     }
  46 
  47     public static volatile Object tmp;
  48 
  49     public static volatile boolean hadError = false;
  50 
  51     private static native int TestCriticalArray0(int[] x);
  52 
  53     public static class Node {
  54         public SoftReference<Node> next;
  55         long payload1;
  56         long payload2;
  57         long payload3;
  58         long payload4;
  59 
  60         public Node(int load) {
  61             payload1 = payload2 = payload3 = payload4 = load;
  62         }
  63     }
  64 
  65     public static void warmUp(long warmupEndTime, int size, long seed) {
  66         Random r = new Random(seed);
  67         // First let the GC assume most of our objects will die.
  68         Node[] roots = new Node[size];
  69 
  70         while (System.currentTimeMillis() < warmupEndTime) {
  71             int index = (int) (r.nextDouble() * roots.length);
  72             roots[index] = new Node(1);
  73         }
  74 
  75         // Make sure the young generation is empty.
  76         for (int i = 0; i < roots.length; ++i) {
  77             roots[i] = null;
  78         }
  79     }
  80 
  81     public static void runTest(long endTime, int size, double alive, long seed) {
  82         Random r = new Random(seed);
  83         final int length = 10000;
  84         int[] array1 = new int[length];
  85         for (int x = 1; x < length; x++) {
  86             array1[x] = x;
  87         }
  88 
  89         Node[] roots = new Node[size];
  90         try {
  91             int index = 0;
  92             roots[0] = new Node(0);
  93 
  94             while (!hadError && (System.currentTimeMillis() < endTime)) {
  95                 int test_val1 = TestCriticalArray0(array1);
  96 
  97                 if (r.nextDouble() > alive) {
  98                     tmp = new Node(test_val1);
  99                 } else {
 100                     index = (int) (r.nextDouble() * roots.length);
 101 
 102                     if (roots[index] != null) {
 103                         Node node = new Node(test_val1);
 104                         node.next = new SoftReference<Node>(roots[index]);
 105                         roots[index] = node;
 106                     } else {
 107                         roots[index] = new Node(test_val1);
 108                     }
 109                 }
 110             }
 111         } catch (OutOfMemoryError e) {
 112             hadError = true;
 113             e.printStackTrace();
 114         }
 115     }
 116 
 117     private static void joinThreads(Thread[] threads) throws Exception {
 118         for (int i = 0; i < threads.length; i++) {
 119             try {
 120                 if (threads[i] != null) {
 121                   threads[i].join();
 122                 }
 123             } catch (InterruptedException e) {
 124                 e.printStackTrace();
 125                 throw e;
 126             }
 127         }
 128     }
 129 
 130     public static void main(String[] args) throws Exception {
 131         if (args.length < 7){
 132             System.out.println("Usage: java TestJNIBlockFullGC <warmupThreads> <warmup-time-in-millis> <warmup iterations> <threads> <time-in-millis> <iterations> <aliveFrac>");
 133             System.exit(0);
 134         }
 135 
 136         int warmupThreads = Integer.parseInt(args[0]);
 137         System.out.println("# Warmup Threads = " + warmupThreads);
 138 
 139         int warmupDuration = Integer.parseInt(args[1]);
 140         System.out.println("WarmUp Duration = " + warmupDuration);
 141         int warmupIterations = Integer.parseInt(args[2]);
 142         System.out.println("# Warmup Iterations = "+ warmupIterations);
 143 
 144         int mainThreads = Integer.parseInt(args[3]);
 145         System.out.println("# Main Threads = " + mainThreads);
 146         int mainDuration = Integer.parseInt(args[4]);
 147         System.out.println("Main Duration = " + mainDuration);
 148         int mainIterations = Integer.parseInt(args[5]);
 149         System.out.println("# Main Iterations = " + mainIterations);
 150 
 151         double liveFrac = Double.parseDouble(args[6]);
 152         System.out.println("Live Fraction = " + liveFrac);
 153 
 154         Thread threads[] = new Thread[Math.max(warmupThreads, mainThreads)];
 155 
 156         System.out.println("Start warm-up threads!");
 157         long warmupStartTime = System.currentTimeMillis();
 158         for (int i = 0; i < warmupThreads; i++) {
 159             long seed = rng.nextLong();
 160             threads[i] = new Thread() {
 161                 public void run() {
 162                     warmUp(warmupStartTime + warmupDuration, warmupIterations, seed);
 163                 };
 164             };
 165             threads[i].start();
 166         }
 167 
 168         joinThreads(threads);
 169 
 170         System.gc();
 171         System.out.println("Keep alive a lot");
 172 
 173         long startTime = System.currentTimeMillis();
 174         for (int i = 0; i < mainThreads; i++) {
 175             long seed = rng.nextLong();
 176             threads[i] = new Thread() {
 177                 public void run() {
 178                     runTest(startTime + mainDuration, mainIterations, liveFrac, seed);
 179                 };
 180             };
 181             threads[i].start();
 182         }
 183         System.out.println("All threads started");
 184 
 185         joinThreads(threads);
 186 
 187         if (hadError) {
 188             throw new RuntimeException("Experienced an OoME during execution.");
 189         }
 190     }
 191 }