1 /* 2 * Copyright (c) 2007, 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 package heapdump.share; 25 26 import java.util.List; 27 import java.util.ArrayList; 28 import java.io.InputStreamReader; 29 import java.io.BufferedReader; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStream; 33 import java.util.Random; 34 35 import vm.share.ProcessUtils; 36 37 import java.util.LinkedList; 38 39 import nsk.share.gc.gp.classload.GeneratedClassProducer; 40 41 /** 42 * This test eats memory by generating random garbage. 43 * <p> 44 * This program can eat either heap or metaspace using 45 * interned strings depending on parameter metaspace. After this, it 46 * can also force JVM to show dump, dump core or execute some command. 47 * The following command line switches are supported: 48 * <p> 49 * "-sleepTime" time to sleep 50 * "-signal" show dump after OOM 51 * "-metaspace" eat metaspace 52 * "-core" dump core after OOM 53 * "-exec command" execute command after OOM 54 */ 55 public class EatMemory { 56 private long sleepTime; 57 private boolean signal; 58 private boolean metaspace; 59 private boolean core; 60 private String exec; 61 private final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 62 private long initialFactor = 50; 63 private long minChunk = 1000; 64 private long factor = 5; 65 private long chunk; 66 private Runtime runtime = Runtime.getRuntime(); 67 private int n = 0; 68 private final int arrayExtraSize = 12; 69 private final int stringLength = 128; 70 private byte[] reserved = new byte[(int) (runtime.maxMemory() / 20)]; 71 private List storage = new ArrayList(); 72 private List strings = new ArrayList(); 73 74 /** 75 * @param sleepTime time to sleep 76 * @param signal true if need to force JVM to show dump (Ctrl-Break / Ctrl-/) after OOM 77 * @param metaspace true if need to eat metaspace 78 * @param core true if need to force JVM to dump core 79 * @param exec command to execute after OOM 80 */ 81 public EatMemory(long sleepTime, boolean signal, boolean metaspace, boolean core, String exec) { 82 this.sleepTime = sleepTime; 83 this.signal = signal; 84 this.metaspace = metaspace; 85 this.core = core; 86 this.exec = exec; 87 } 88 89 private int getSize(long chunk, long factor) { 90 return (int) Math.min(Integer.MAX_VALUE, (chunk - arrayExtraSize) / factor); 91 } 92 93 private Object create(long chunk) { 94 switch (++n % 8) { 95 case 0: 96 return new byte[getSize(chunk, 1)]; 97 case 1: 98 return new short[getSize(chunk, 2)]; 99 case 2: 100 return new char[getSize(chunk, 2)]; 101 case 3: 102 return new boolean[getSize(chunk, 1)]; 103 case 4: 104 return new long[getSize(chunk, 8)]; 105 case 5: 106 return new float[getSize(chunk, 4)]; 107 case 6: 108 return new double[getSize(chunk, 8)]; 109 case 7: 110 return new Object[getSize(chunk, 16)]; 111 default: 112 // Should never happen 113 return null; 114 } 115 } 116 117 118 public void eatHeap() { 119 try { 120 int[][] arrays = new int[Integer.MAX_VALUE / 2][]; 121 for (int i = 0; ; ++i) { 122 arrays[i] = new int[Integer.MAX_VALUE / 2]; 123 } 124 } catch (OutOfMemoryError x) { 125 reserved = null; 126 } 127 } 128 129 public void eatMetaspace() { 130 try { 131 System.out.println("Starting eating metaspace..."); 132 GeneratedClassProducer gp = new GeneratedClassProducer(); 133 List<Class> lst = new LinkedList<Class>(); 134 System.out.println("... Oh, so tasty!"); 135 while (true) { 136 lst.add(gp.create(0)); 137 } 138 } catch (OutOfMemoryError e) { 139 } 140 } 141 142 public void eatMemory() throws IOException { 143 if (metaspace) 144 eatMetaspace(); 145 else 146 eatHeap(); 147 reserved = null; 148 } 149 150 /** 151 * Sleep some time to give system time to process a signal, start 152 * process, etc. 153 */ 154 private void sleepSome() { 155 try { 156 Thread.sleep(sleepTime); 157 } catch (InterruptedException e) { 158 } 159 } 160 161 /** 162 * Throws an exception if execution was not successful. 163 */ 164 private void execute() throws IOException, InterruptedException { 165 int pid = ProcessUtils.getPid(); 166 if (pid < 0) { 167 throw new RuntimeException("Negative pid " + pid + "; Failed to executed " + exec); 168 } 169 exec = exec.replaceAll("%p", Integer.toString(pid)); 170 System.out.println("Executing " + exec); 171 Process process = Runtime.getRuntime().exec(exec); 172 sleepSome(); 173 process.waitFor(); 174 StringBuilder sb = copy(process.getInputStream(), System.out); 175 sb.append(copy(process.getErrorStream(), System.out)); 176 if (process.exitValue() != 0) { 177 // trying provide as much informative failure string 178 // hoping, it will be the last line in the error stream... 179 180 181 String failureCause = "Unknown"; 182 String allTheOutput = sb.toString(); 183 String[] lines = allTheOutput.split(System.getProperty("line.separator")); 184 185 for (int i = lines.length - 1; i >= 0; i--) { 186 // Check that string is not empty 187 if (!lines[i].trim().equals("")) { 188 failureCause = lines[i]; 189 break; 190 } 191 } 192 throw new RuntimeException(failureCause); 193 } 194 } 195 196 private StringBuilder copy(InputStream in, OutputStream out) throws IOException { 197 byte[] buff = new byte[1000]; 198 StringBuilder sb = new StringBuilder(); 199 while (in.available() != 0) { 200 n = in.read(buff, 0, buff.length); 201 out.write(buff, 0, n); 202 sb.append(new String(buff, 0, n)); 203 } 204 return sb; 205 } 206 207 public void run() throws Exception { 208 eatMemory(); 209 if (signal) { 210 ProcessUtils.sendCtrlBreak(); 211 sleepSome(); 212 213 } 214 if (exec != null) { 215 execute(); 216 } 217 if (core) { 218 /* 219 * We try to dump core here. 220 * On Unix systems a signal is sent to the process. We need to wait some time 221 * to give time to process it. On Windows systems, core dump is not supported 222 * and we just do not do anything in this case. 223 */ 224 boolean res = ProcessUtils.dumpCore(); 225 if (res) { 226 sleepSome(); 227 throw new RuntimeException("Signal sent, but core was not dumped"); 228 } 229 } 230 } 231 232 public static void main(String[] args) throws Exception { 233 long sleepTime = 5000; 234 boolean signal = false; 235 boolean metaspace = false; 236 boolean core = false; 237 String exec = null; 238 for (int i = 0; i < args.length; ++i) { 239 if (args[i].equalsIgnoreCase("-sleepTime")) { 240 if (++i < args.length) { 241 long time = Long.parseLong(args[i]); 242 if (time != 0) 243 sleepTime = time; 244 } else 245 throw new RuntimeException("Argument expected after -sleepTime"); 246 } 247 if (args[i].equalsIgnoreCase("-signal")) 248 signal = true; 249 if (args[i].equalsIgnoreCase("-metaspace")) 250 metaspace = true; 251 if (args[i].equalsIgnoreCase("-core")) 252 core = true; 253 if (args[i].equalsIgnoreCase("-exec")) { 254 if (++i < args.length) 255 exec = args[i]; 256 else 257 throw new RuntimeException("Argument expected after -exec"); 258 } 259 } 260 new EatMemory(sleepTime, signal, metaspace, core, exec).run(); 261 } 262 }