1 /* 2 * Copyright (c) 2014, 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 26 * @bug 6857566 27 * @summary DirectByteBuffer garbage creation can outpace reclamation 28 * 29 * @run main/othervm -XX:MaxDirectMemorySize=128m DirectBufferAllocTest 30 */ 31 32 import java.nio.ByteBuffer; 33 import java.util.List; 34 import java.util.concurrent.*; 35 import java.util.stream.Collectors; 36 import java.util.stream.IntStream; 37 38 public class DirectBufferAllocTest { 39 // defaults 40 static final int RUN_TIME_SECONDS = 5; 41 static final int MIN_THREADS = 4; 42 static final int MAX_THREADS = 64; 43 static final int CAPACITY = 1024 * 1024; // bytes 44 45 /** 46 * This test spawns multiple threads that constantly allocate direct 47 * {@link ByteBuffer}s in a loop, trying to provoke {@link OutOfMemoryError}.<p> 48 * When run without command-line arguments, it runs as a regression test 49 * for at most 5 seconds.<p> 50 * Command line arguments: 51 * <pre> 52 * -r run-time-seconds <i>(duration of successful test - default 5 s)</i> 53 * -t threads <i>(default is 2 * # of CPUs, at least 4 but no more than 64)</i> 54 * -c capacity <i>(of direct buffers in bytes - default is 1MB)</i> 55 * -p print-alloc-time-batch-size <i>(every "batch size" iterations, 56 * average time per allocation is printed)</i> 57 * </pre> 58 * Use something like the following to run a 10 minute stress test and 59 * print allocation times as it goes: 60 * <pre> 61 * java -XX:MaxDirectMemorySize=128m DirectBufferAllocTest -r 600 -t 32 -p 5000 62 * </pre> 63 */ 64 public static void main(String[] args) throws Exception { 65 int runTimeSeconds = RUN_TIME_SECONDS; 66 int threads = Math.max( 67 Math.min( 68 Runtime.getRuntime().availableProcessors() * 2, 69 MAX_THREADS 70 ), 71 MIN_THREADS 72 ); 73 int capacity = CAPACITY; 74 int printBatchSize = 0; 75 76 // override with command line arguments 77 for (int i = 0; i < args.length; i++) { 78 switch (args[i]) { 79 case "-r": 80 runTimeSeconds = Integer.parseInt(args[++i]); 81 break; 82 case "-t": 83 threads = Integer.parseInt(args[++i]); 84 break; 85 case "-c": 86 capacity = Integer.parseInt(args[++i]); 87 break; 88 case "-p": 89 printBatchSize = Integer.parseInt(args[++i]); 90 break; 91 default: 92 System.err.println( 93 "Usage: java" + 94 " [-XX:MaxDirectMemorySize=XXXm]" + 95 " DirectBufferAllocTest" + 96 " [-r run-time-seconds]" + 97 " [-t threads]" + 98 " [-c capacity-of-direct-buffers]" + 99 " [-p print-alloc-time-batch-size]" 100 ); 101 System.exit(-1); 102 } 103 } 104 105 System.out.printf( 106 "Allocating direct ByteBuffers with capacity %d bytes, using %d threads for %d seconds...\n", 107 capacity, threads, runTimeSeconds 108 ); 109 110 ExecutorService executor = Executors.newFixedThreadPool(threads); 111 112 int pbs = printBatchSize; 113 int cap = capacity; 114 115 List<Future<Void>> futures = 116 IntStream.range(0, threads) 117 .mapToObj( 118 i -> (Callable<Void>) () -> { 119 long t0 = System.nanoTime(); 120 loop: 121 while (true) { 122 for (int n = 0; pbs == 0 || n < pbs; n++) { 123 if (Thread.interrupted()) { 124 break loop; 125 } 126 ByteBuffer.allocateDirect(cap); 127 } 128 long t1 = System.nanoTime(); 129 if (pbs > 0) { 130 System.out.printf( 131 "Thread %2d: %5.2f ms/allocation\n", 132 i, ((double) (t1 - t0) / (1_000_000d * pbs)) 133 ); 134 } 135 t0 = t1; 136 } 137 return null; 138 } 139 ) 140 .map(executor::submit) 141 .collect(Collectors.toList()); 142 143 for (int i = 0; i < runTimeSeconds; i++) { 144 if (futures.stream().anyMatch(Future::isDone)) { 145 break; 146 } 147 Thread.sleep(1000L); 148 } 149 150 Exception exception = null; 151 for (Future<Void> future : futures) { 152 if (future.isDone()) { 153 try { 154 future.get(); 155 } catch (ExecutionException e) { 156 if (exception == null) { 157 exception = new RuntimeException("Errors encountered!"); 158 } 159 exception.addSuppressed(e.getCause()); 160 } 161 } else { 162 future.cancel(true); 163 } 164 } 165 166 executor.shutdown(); 167 168 if (exception != null) { 169 throw exception; 170 } else { 171 System.out.printf("No errors after %d seconds.\n", runTimeSeconds); 172 } 173 } 174 }