/* * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * @test * @bug 6857566 * @summary DirectByteBuffer garbage creation can outpace reclamation * * @run main/othervm -XX:MaxDirectMemorySize=128m DirectBufferAllocTest */ import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.*; import java.util.stream.Collectors; import java.util.stream.IntStream; public class DirectBufferAllocTest { // defaults static final int RUN_TIME_SECONDS = 5; static final int MIN_THREADS = 4; static final int MAX_THREADS = 64; static final int CAPACITY = 1024 * 1024; // bytes /** * This test spawns multiple threads that constantly allocate direct * {@link ByteBuffer}s in a loop, trying to provoke {@link OutOfMemoryError}.

* When run without command-line arguments, it runs as a regression test * for at most 5 seconds.

* Command line arguments: *

     * -r run-time-seconds (duration of successful test - default 5 s)
     * -t threads (default is 2 * # of CPUs, at least 4 but no more than 64)
     * -c capacity (of direct buffers in bytes - default is 1MB)
     * -p print-alloc-time-batch-size (every "batch size" iterations,
     *                                 average time per allocation is printed)
     * 
* Use something like the following to run a 10 minute stress test and * print allocation times as it goes: *
     * java -XX:MaxDirectMemorySize=128m DirectBufferAllocTest -r 600 -t 32 -p 5000
     * 
*/ public static void main(String[] args) throws Exception { int runTimeSeconds = RUN_TIME_SECONDS; int threads = Math.max( Math.min( Runtime.getRuntime().availableProcessors() * 2, MAX_THREADS ), MIN_THREADS ); int capacity = CAPACITY; int printBatchSize = 0; // override with command line arguments for (int i = 0; i < args.length; i++) { switch (args[i]) { case "-r": runTimeSeconds = Integer.parseInt(args[++i]); break; case "-t": threads = Integer.parseInt(args[++i]); break; case "-c": capacity = Integer.parseInt(args[++i]); break; case "-p": printBatchSize = Integer.parseInt(args[++i]); break; default: System.err.println( "Usage: java" + " [-XX:MaxDirectMemorySize=XXXm]" + " DirectBufferAllocTest" + " [-r run-time-seconds]" + " [-t threads]" + " [-c capacity-of-direct-buffers]" + " [-p print-alloc-time-batch-size]" ); System.exit(-1); } } System.out.printf( "Allocating direct ByteBuffers with capacity %d bytes, using %d threads for %d seconds...\n", capacity, threads, runTimeSeconds ); ExecutorService executor = Executors.newFixedThreadPool(threads); int pbs = printBatchSize; int cap = capacity; List> futures = IntStream.range(0, threads) .mapToObj( i -> (Callable) () -> { long t0 = System.nanoTime(); loop: while (true) { for (int n = 0; pbs == 0 || n < pbs; n++) { if (Thread.interrupted()) { break loop; } ByteBuffer.allocateDirect(cap); } long t1 = System.nanoTime(); if (pbs > 0) { System.out.printf( "Thread %2d: %5.2f ms/allocation\n", i, ((double) (t1 - t0) / (1_000_000d * pbs)) ); } t0 = t1; } return null; } ) .map(executor::submit) .collect(Collectors.toList()); for (int i = 0; i < runTimeSeconds; i++) { if (futures.stream().anyMatch(Future::isDone)) { break; } Thread.sleep(1000L); } Exception exception = null; for (Future future : futures) { if (future.isDone()) { try { future.get(); } catch (ExecutionException e) { if (exception == null) { exception = new RuntimeException("Errors encountered!"); } exception.addSuppressed(e.getCause()); } } else { future.cancel(true); } } executor.shutdown(); if (exception != null) { throw exception; } else { System.out.printf("No errors after %d seconds.\n", runTimeSeconds); } } }