1 /* 2 * Copyright (c) 2017, 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 // Stress the GC locker by calling GetPrimitiveArrayCritical while 26 // concurrently filling up old gen. 27 28 import java.lang.management.MemoryPoolMXBean; 29 import java.lang.management.ManagementFactory; 30 import java.lang.management.MemoryUsage; 31 import java.nio.ByteBuffer; 32 import java.util.ArrayDeque; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Queue; 37 38 final class ThreadUtils { 39 public static void sleep(long durationMS) { 40 try { 41 Thread.sleep(durationMS); 42 } catch (Exception e) { 43 } 44 } 45 } 46 47 class Filler { 48 private static final int SIZE = 250000; 49 50 private int[] i1 = new int[SIZE]; 51 private int[] i2 = new int[SIZE]; 52 private short[] s1 = new short[SIZE]; 53 private short[] s2 = new short[SIZE]; 54 55 private Map<Object, Object> map = new HashMap<>(); 56 57 public Filler() { 58 for (int i = 0; i < 10000; i++) { 59 map.put(new Object(), new Object()); 60 } 61 } 62 } 63 64 class Exitable { 65 private volatile boolean shouldExit = false; 66 67 protected boolean shouldExit() { 68 return shouldExit; 69 } 70 71 public void exit() { 72 shouldExit = true; 73 } 74 } 75 76 class MemoryWatcher { 77 private MemoryPoolMXBean bean; 78 private final int thresholdPromille = 750; 79 private final int criticalThresholdPromille = 800; 80 private final int minGCWaitMS = 1000; 81 private final int minFreeWaitElapsedMS = 30000; 82 private final int minFreeCriticalWaitMS = 500; 83 84 private int lastUsage = 0; 85 private long lastGCDetected = System.currentTimeMillis(); 86 private long lastFree = System.currentTimeMillis(); 87 88 public MemoryWatcher(String mxBeanName) { 89 List<MemoryPoolMXBean> memoryBeans = ManagementFactory.getMemoryPoolMXBeans(); 90 for (MemoryPoolMXBean bean : memoryBeans) { 91 if (bean.getName().equals(mxBeanName)) { 92 this.bean = bean; 93 break; 94 } 95 } 96 } 97 98 private int getMemoryUsage() { 99 if (bean == null) { 100 Runtime r = Runtime.getRuntime(); 101 float free = (float) r.freeMemory() / r.maxMemory(); 102 return Math.round((1 - free) * 1000); 103 } else { 104 MemoryUsage usage = bean.getUsage(); 105 float used = (float) usage.getUsed() / usage.getCommitted(); 106 return Math.round(used * 1000); 107 } 108 } 109 110 public synchronized boolean shouldFreeUpSpace() { 111 int usage = getMemoryUsage(); 112 long now = System.currentTimeMillis(); 113 114 boolean detectedGC = false; 115 if (usage < lastUsage) { 116 lastGCDetected = now; 117 detectedGC = true; 118 } 119 120 lastUsage = usage; 121 122 long elapsed = now - lastFree; 123 long timeSinceLastGC = now - lastGCDetected; 124 125 if (usage > criticalThresholdPromille && elapsed > minFreeCriticalWaitMS) { 126 lastFree = now; 127 return true; 128 } else if (usage > thresholdPromille && !detectedGC) { 129 if (elapsed > minFreeWaitElapsedMS || timeSinceLastGC > minGCWaitMS) { 130 lastFree = now; 131 return true; 132 } 133 } 134 135 return false; 136 } 137 } 138 139 class MemoryUser extends Exitable implements Runnable { 140 private final Queue<Filler> cache = new ArrayDeque<Filler>(); 141 private final MemoryWatcher watcher; 142 143 private void load() { 144 if (watcher.shouldFreeUpSpace()) { 145 int toRemove = cache.size() / 5; 146 for (int i = 0; i < toRemove; i++) { 147 cache.remove(); 148 } 149 } 150 cache.add(new Filler()); 151 } 152 153 public MemoryUser(String mxBeanName) { 154 watcher = new MemoryWatcher(mxBeanName); 155 } 156 157 @Override 158 public void run() { 159 for (int i = 0; i < 200; i++) { 160 load(); 161 } 162 163 while (!shouldExit()) { 164 load(); 165 } 166 } 167 } 168 169 class GCLockerStresser extends Exitable implements Runnable { 170 static native void fillWithRandomValues(byte[] array); 171 172 @Override 173 public void run() { 174 byte[] array = new byte[1024 * 1024]; 175 while (!shouldExit()) { 176 fillWithRandomValues(array); 177 } 178 } 179 } 180 181 public class TestGCLocker { 182 private static Exitable startGCLockerStresser(String name) { 183 GCLockerStresser task = new GCLockerStresser(); 184 185 Thread thread = new Thread(task); 186 thread.setName(name); 187 thread.setPriority(Thread.MIN_PRIORITY); 188 thread.start(); 189 190 return task; 191 } 192 193 private static Exitable startMemoryUser(String mxBeanName) { 194 MemoryUser task = new MemoryUser(mxBeanName); 195 196 Thread thread = new Thread(task); 197 thread.setName("Memory User"); 198 thread.start(); 199 200 return task; 201 } 202 203 public static void main(String[] args) { 204 System.loadLibrary("TestGCLocker"); 205 206 long durationMinutes = args.length > 0 ? Long.parseLong(args[0]) : 5; 207 String mxBeanName = args.length > 1 ? args[1] : null; 208 209 long startMS = System.currentTimeMillis(); 210 211 Exitable stresser1 = startGCLockerStresser("GCLockerStresser1"); 212 Exitable stresser2 = startGCLockerStresser("GCLockerStresser2"); 213 Exitable memoryUser = startMemoryUser(mxBeanName); 214 215 long durationMS = durationMinutes * 60 * 1000; 216 while ((System.currentTimeMillis() - startMS) < durationMS) { 217 ThreadUtils.sleep(10 * 1010); 218 } 219 220 stresser1.exit(); 221 stresser2.exit(); 222 memoryUser.exit(); 223 } 224 }