1 /*
   2  * Copyright (c) 2016, 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  * @test TestStressIHOPMultiThread
  26  * @bug 8148397
  27  * @key stress
  28  * @summary Stress test for IHOP
  29  * @requires vm.gc.G1
  30  * @run main/othervm/timeout=200 -Xmx128m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
  31  *              -XX:+UseG1GC -XX:G1HeapRegionSize=1m -XX:+G1UseAdaptiveIHOP
  32  *              -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread1.log
  33  *              -Dtimeout=2 -DheapUsageMinBound=30 -DheapUsageMaxBound=80
  34  *              -Dthreads=2 TestStressIHOPMultiThread
  35  * @run main/othervm/timeout=200 -Xmx256m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
  36  *              -XX:+UseG1GC -XX:G1HeapRegionSize=2m -XX:+G1UseAdaptiveIHOP
  37  *              -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread2.log
  38  *              -Dtimeout=2 -DheapUsageMinBound=60 -DheapUsageMaxBound=90
  39  *              -Dthreads=3 TestStressIHOPMultiThread
  40  * @run main/othervm/timeout=200 -Xmx256m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
  41  *              -XX:+UseG1GC -XX:G1HeapRegionSize=4m -XX:-G1UseAdaptiveIHOP
  42  *              -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread3.log
  43  *              -Dtimeout=2 -DheapUsageMinBound=40 -DheapUsageMaxBound=90
  44  *              -Dthreads=5 TestStressIHOPMultiThread
  45  * @run main/othervm/timeout=200 -Xmx128m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
  46  *              -XX:+UseG1GC -XX:G1HeapRegionSize=8m -XX:+G1UseAdaptiveIHOP
  47  *              -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread4.log
  48  *              -Dtimeout=2 -DheapUsageMinBound=20 -DheapUsageMaxBound=90
  49  *              -Dthreads=10 TestStressIHOPMultiThread
  50  * @run main/othervm/timeout=200 -Xmx512m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
  51  *              -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:+G1UseAdaptiveIHOP
  52  *              -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread5.log
  53  *              -Dtimeout=2 -DheapUsageMinBound=20 -DheapUsageMaxBound=90
  54  *              -Dthreads=17 TestStressIHOPMultiThread
  55  */
  56 
  57 import java.util.ArrayList;
  58 import java.util.LinkedList;
  59 import java.util.List;
  60 
  61 /**
  62  * Stress test for Adaptive IHOP. Starts a number of threads that fill and free
  63  * specified amount of memory. Tests work with enabled IHOP logging.
  64  *
  65  */
  66 public class TestStressIHOPMultiThread {
  67 
  68     public final static List<Object> GARBAGE = new LinkedList<>();
  69 
  70     private final long HEAP_SIZE;
  71     // Amount of memory to be allocated before iterations start
  72     private final long HEAP_PREALLOC_SIZE;
  73     // Amount of memory to be allocated and freed during iterations
  74     private final long HEAP_ALLOC_SIZE;
  75     private final int CHUNK_SIZE = 100000;
  76 
  77     private final int TIMEOUT;
  78     private final int THREADS;
  79     private final int HEAP_LOW_BOUND;
  80     private final int HEAP_HIGH_BOUND;
  81 
  82     private volatile boolean running = true;
  83     private final List<AllocationThread> threads;
  84 
  85     public static void main(String[] args) throws InterruptedException {
  86         new TestStressIHOPMultiThread().start();
  87 
  88     }
  89 
  90     TestStressIHOPMultiThread() {
  91 
  92         TIMEOUT = Integer.getInteger("timeout") * 60;
  93         THREADS = Integer.getInteger("threads");
  94         HEAP_LOW_BOUND = Integer.getInteger("heapUsageMinBound");
  95         HEAP_HIGH_BOUND = Integer.getInteger("heapUsageMaxBound");
  96         HEAP_SIZE = Runtime.getRuntime().maxMemory();
  97 
  98         HEAP_PREALLOC_SIZE = HEAP_SIZE * HEAP_LOW_BOUND / 100;
  99         HEAP_ALLOC_SIZE = HEAP_SIZE * (HEAP_HIGH_BOUND - HEAP_LOW_BOUND) / 100;
 100 
 101         threads = new ArrayList<>(THREADS);
 102     }
 103 
 104     public void start() throws InterruptedException {
 105         fill();
 106         createThreads();
 107         waitForStress();
 108         stressDone();
 109         waitForFinish();
 110     }
 111 
 112     /**
 113      * Fills HEAP_PREALLOC_SIZE bytes of garbage.
 114      */
 115     private void fill() {
 116         long allocated = 0;
 117         while (allocated < HEAP_PREALLOC_SIZE) {
 118             GARBAGE.add(new byte[CHUNK_SIZE]);
 119             allocated += CHUNK_SIZE;
 120         }
 121     }
 122 
 123     /**
 124      * Creates a number of threads which will fill and free amount of memory.
 125      */
 126     private void createThreads() {
 127         for (int i = 0; i < THREADS; ++i) {
 128             System.out.println("Create thread " + i);
 129             AllocationThread thread =new TestStressIHOPMultiThread.AllocationThread(i, HEAP_ALLOC_SIZE / THREADS);
 130             // Put reference to thread garbage into common garbage for avoiding possible optimization.
 131             GARBAGE.add(thread.getList());
 132             threads.add(thread);
 133         }
 134         threads.forEach(t -> t.start());
 135     }
 136 
 137     /**
 138      * Wait each thread for finishing
 139      */
 140     private void waitForFinish() {
 141         threads.forEach(thread -> {
 142             thread.silentJoin();
 143         });
 144     }
 145 
 146     private boolean isRunning() {
 147         return running;
 148     }
 149 
 150     private void stressDone() {
 151         running = false;
 152     }
 153 
 154     private void waitForStress() throws InterruptedException {
 155         Thread.sleep(TIMEOUT * 1000);
 156     }
 157 
 158     private class AllocationThread extends Thread {
 159 
 160         private final List<Object> garbage;
 161 
 162         private final long amountOfGarbage;
 163         private final int threadId;
 164 
 165         public AllocationThread(int id, long amount) {
 166             super("Thread " + id);
 167             threadId = id;
 168             amountOfGarbage = amount;
 169             garbage = new LinkedList<>();
 170         }
 171 
 172         /**
 173          * Returns list of garbage.
 174          * @return List with thread garbage.
 175          */
 176         public List<Object> getList(){
 177             return garbage;
 178         }
 179 
 180         @Override
 181         public void run() {
 182             System.out.println("Start the thread " + threadId);
 183             while (TestStressIHOPMultiThread.this.isRunning()) {
 184                 try {
 185                     allocate(amountOfGarbage);
 186                 } catch (OutOfMemoryError e) {
 187                     free();
 188                     System.out.println("OutOfMemoryError occurred in thread " + threadId);
 189                     break;
 190                 }
 191                 free();
 192             }
 193         }
 194 
 195         private void silentJoin() {
 196             System.out.println("Join the thread " + threadId);
 197             try {
 198                 join();
 199             } catch (InterruptedException ie) {
 200                 throw new RuntimeException(ie);
 201             }
 202         }
 203 
 204         /**
 205          * Allocates thread local garbage
 206          */
 207         private void allocate(long amount) {
 208             long allocated = 0;
 209             while (allocated < amount && TestStressIHOPMultiThread.this.isRunning()) {
 210                 garbage.add(new byte[CHUNK_SIZE]);
 211                 allocated += CHUNK_SIZE;
 212             }
 213         }
 214 
 215         /**
 216          * Frees thread local garbage
 217          */
 218         private void free() {
 219             garbage.clear();
 220         }
 221     }
 222 }