--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/nio/ch/TestMaxCachedBufferSize.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+import java.io.IOException;
+
+import java.lang.management.BufferPoolMXBean;
+import java.lang.management.ManagementFactory;
+
+import java.nio.ByteBuffer;
+
+import java.nio.channels.FileChannel;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static java.nio.file.StandardOpenOption.CREATE;
+import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
+import static java.nio.file.StandardOpenOption.WRITE;
+
+import java.util.List;
+import java.util.Random;
+
+/*
+ * @test
+ * @requires sun.arch.data.model == "64"
+ * @modules java.management
+ * @build TestMaxCachedBufferSize
+ * @run main/othervm TestMaxCachedBufferSize
+ * @run main/othervm -Djdk.nio.maxCachedBufferSize=0 TestMaxCachedBufferSize
+ * @run main/othervm -Djdk.nio.maxCachedBufferSize=2000 TestMaxCachedBufferSize
+ * @run main/othervm -Djdk.nio.maxCachedBufferSize=100000 TestMaxCachedBufferSize
+ * @run main/othervm -Djdk.nio.maxCachedBufferSize=10000000 TestMaxCachedBufferSize
+ * @summary Test the implementation of the jdk.nio.maxCachedBufferSize property.
+ */
+public class TestMaxCachedBufferSize {
+ private static final int DEFAULT_ITERS = 10 * 1000;
+ private static final int DEFAULT_THREAD_NUM = 4;
+
+ private static final int SMALL_BUFFER_MIN_SIZE = 4 * 1024;
+ private static final int SMALL_BUFFER_MAX_SIZE = 64 * 1024;
+ private static final int SMALL_BUFFER_DIFF_SIZE =
+ SMALL_BUFFER_MAX_SIZE - SMALL_BUFFER_MIN_SIZE;
+
+ private static final int LARGE_BUFFER_MIN_SIZE = 512 * 1024;
+ private static final int LARGE_BUFFER_MAX_SIZE = 4 * 1024 * 1024;
+ private static final int LARGE_BUFFER_DIFF_SIZE =
+ LARGE_BUFFER_MAX_SIZE - LARGE_BUFFER_MIN_SIZE;
+
+ private static final int LARGE_BUFFER_FREQUENCY = 100;
+
+ private static final String FILE_NAME_PREFIX = "nio-out-file-";
+ private static final int VERBOSE_PERIOD = 5 * 1000;
+
+ private static int iters = DEFAULT_ITERS;
+ private static int threadNum = DEFAULT_THREAD_NUM;
+
+ private static BufferPoolMXBean getDirectPool() {
+ final List<BufferPoolMXBean> pools =
+ ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
+ for (BufferPoolMXBean pool : pools) {
+ if (pool.getName().equals("direct")) {
+ return pool;
+ }
+ }
+ throw new Error("could not find direct pool");
+ }
+ private static final BufferPoolMXBean directPool = getDirectPool();
+
+ // Each worker will do write operations on a file channel using
+ // buffers of various sizes. The buffer size is randomly chosen to
+ // be within a small or a large range. This way we can control
+ // which buffers can be cached (all, only the small ones, or none)
+ // by setting the jdk.nio.maxCachedBufferSize property.
+ private static class Worker implements Runnable {
+ private final int id;
+ private final Random random = new Random();
+ private long smallBufferCount = 0;
+ private long largeBufferCount = 0;
+
+ private int getWriteSize() {
+ int minSize = 0;
+ int diff = 0;
+ if (random.nextInt() % LARGE_BUFFER_FREQUENCY != 0) {
+ // small buffer
+ minSize = SMALL_BUFFER_MIN_SIZE;
+ diff = SMALL_BUFFER_DIFF_SIZE;
+ smallBufferCount += 1;
+ } else {
+ // large buffer
+ minSize = LARGE_BUFFER_MIN_SIZE;
+ diff = LARGE_BUFFER_DIFF_SIZE;
+ largeBufferCount += 1;
+ }
+ return minSize + random.nextInt(diff);
+ }
+
+ private void loop() {
+ final String fileName = String.format("%s%d", FILE_NAME_PREFIX, id);
+
+ try {
+ for (int i = 0; i < iters; i += 1) {
+ final int writeSize = getWriteSize();
+
+ // This will allocate a HeapByteBuffer. It should not
+ // be a direct buffer, otherwise the write() method on
+ // the channel below will not create a temporary
+ // direct buffer for the write.
+ final ByteBuffer buffer = ByteBuffer.allocate(writeSize);
+
+ // Put some random data on it.
+ while (buffer.hasRemaining()) {
+ buffer.put((byte) random.nextInt());
+ }
+ buffer.rewind();
+
+ final Path file = Paths.get(fileName);
+ try (FileChannel outChannel = FileChannel.open(file, CREATE, TRUNCATE_EXISTING, WRITE)) {
+ // The write() method will create a temporary
+ // direct buffer for the write and attempt to cache
+ // it. It's important that buffer is not a
+ // direct buffer, otherwise the temporary buffer
+ // will not be created.
+ long res = outChannel.write(buffer);
+ }
+
+ if ((i + 1) % VERBOSE_PERIOD == 0) {
+ System.out.printf(
+ " Worker %3d | %8d Iters | Small %8d Large %8d | Direct %4d / %7dK\n",
+ id, i + 1, smallBufferCount, largeBufferCount,
+ directPool.getCount(), directPool.getTotalCapacity() / 1024);
+ }
+ }
+ } catch (IOException e) {
+ throw new Error("I/O error", e);
+ }
+ }
+
+ @Override
+ public void run() {
+ loop();
+ }
+
+ public Worker(int id) {
+ this.id = id;
+ }
+ }
+
+ public static void checkDirectBuffers(long expectedCount, long expectedMax) {
+ final long directCount = directPool.getCount();
+ final long directTotalCapacity = directPool.getTotalCapacity();
+ System.out.printf("Direct %d / %dK\n",
+ directCount, directTotalCapacity / 1024);
+
+ // Note that directCount could be < expectedCount. This can
+ // happen if a GC occurs after one of the worker threads exits
+ // since its thread-local DirectByteBuffer could be cleaned up
+ // before we reach here.
+ if (directCount > expectedCount) {
+ throw new Error(String.format(
+ "inconsistent direct buffer total count, expected = %d, found = %d",
+ expectedCount, directCount));
+ }
+
+ if (directTotalCapacity > expectedMax) {
+ throw new Error(String.format(
+ "inconsistent direct buffer total capacity, expectex max = %d, found = %d",
+ expectedMax, directTotalCapacity));
+ }
+ }
+
+ public static void main(String[] args) {
+ final String maxBufferSizeStr = System.getProperty("jdk.nio.maxCachedBufferSize");
+ final long maxBufferSize =
+ (maxBufferSizeStr != null) ? Long.valueOf(maxBufferSizeStr) : Long.MAX_VALUE;
+
+ // We assume that the max cannot be equal to a size of a
+ // buffer that can be allocated (makes sanity checking at the
+ // end easier).
+ if ((SMALL_BUFFER_MIN_SIZE <= maxBufferSize &&
+ maxBufferSize <= SMALL_BUFFER_MAX_SIZE) ||
+ (LARGE_BUFFER_MIN_SIZE <= maxBufferSize &&
+ maxBufferSize <= LARGE_BUFFER_MAX_SIZE)) {
+ throw new Error(String.format("max buffer size = %d not allowed",
+ maxBufferSize));
+ }
+
+ System.out.printf("Threads %d | Iterations %d | MaxBufferSize %d\n",
+ threadNum, iters, maxBufferSize);
+ System.out.println();
+
+ final Thread[] threads = new Thread[threadNum];
+ for (int i = 0; i < threadNum; i += 1) {
+ threads[i] = new Thread(new Worker(i));
+ threads[i].start();
+ }
+
+ try {
+ for (int i = 0; i < threadNum; i += 1) {
+ threads[i].join();
+ }
+ } catch (InterruptedException e) {
+ throw new Error("join() interrupted!", e);
+ }
+
+ // There is an assumption here that, at this point, only the
+ // cached DirectByteBuffers should be active. Given we
+ // haven't used any other DirectByteBuffers in this test, this
+ // should hold.
+ //
+ // Also note that we can only do the sanity checking at the
+ // end and not during the run given that, at any time, there
+ // could be buffers currently in use by some of the workers
+ // that will not be cached.
+
+ System.out.println();
+ if (maxBufferSize < SMALL_BUFFER_MAX_SIZE) {
+ // The max buffer size is smaller than all buffers that
+ // were allocated. No buffers should have been cached.
+ checkDirectBuffers(0, 0);
+ } else if (maxBufferSize < LARGE_BUFFER_MIN_SIZE) {
+ // The max buffer size is larger than all small buffers
+ // but smaller than all large buffers that were
+ // allocated. Only small buffers could have been cached.
+ checkDirectBuffers(threadNum,
+ (long) threadNum * (long) SMALL_BUFFER_MAX_SIZE);
+ } else {
+ // The max buffer size is larger than all buffers that
+ // were allocated. All buffers could have been cached.
+ checkDirectBuffers(threadNum,
+ (long) threadNum * (long) LARGE_BUFFER_MAX_SIZE);
+ }
+ }
+}