Path: blob/master/test/jdk/sun/nio/ch/TestMaxCachedBufferSize.java
41149 views
/*1* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.IOException;24import java.lang.management.BufferPoolMXBean;25import java.lang.management.ManagementFactory;26import java.nio.ByteBuffer;27import java.nio.channels.FileChannel;28import java.nio.file.Path;29import java.nio.file.Paths;30import java.util.List;31import java.util.SplittableRandom;32import java.util.concurrent.CountDownLatch;3334import static java.nio.file.StandardOpenOption.CREATE;35import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;36import static java.nio.file.StandardOpenOption.WRITE;3738import jdk.test.lib.RandomFactory;3940/*41* @test42* @requires sun.arch.data.model == "64"43* @modules java.management44* @library /test/lib45* @build TestMaxCachedBufferSize46* @run main/othervm/timeout=150 TestMaxCachedBufferSize47* @run main/othervm/timeout=150 -Djdk.nio.maxCachedBufferSize=0 TestMaxCachedBufferSize48* @run main/othervm/timeout=150 -Djdk.nio.maxCachedBufferSize=2000 TestMaxCachedBufferSize49* @run main/othervm/timeout=150 -Djdk.nio.maxCachedBufferSize=100000 TestMaxCachedBufferSize50* @run main/othervm/timeout=150 -Djdk.nio.maxCachedBufferSize=10000000 TestMaxCachedBufferSize51* @summary Test the implementation of the jdk.nio.maxCachedBufferSize property52* (use -Dseed=X to set PRNG seed)53* @key randomness54*/55public class TestMaxCachedBufferSize {56private static final int DEFAULT_ITERS = 5 * 1000;57private static final int DEFAULT_THREAD_NUM = 4;5859private static final int SMALL_BUFFER_MIN_SIZE = 4 * 1024;60private static final int SMALL_BUFFER_MAX_SIZE = 64 * 1024;61private static final int SMALL_BUFFER_DIFF_SIZE =62SMALL_BUFFER_MAX_SIZE - SMALL_BUFFER_MIN_SIZE;6364private static final int LARGE_BUFFER_MIN_SIZE = 512 * 1024;65private static final int LARGE_BUFFER_MAX_SIZE = 4 * 1024 * 1024;66private static final int LARGE_BUFFER_DIFF_SIZE =67LARGE_BUFFER_MAX_SIZE - LARGE_BUFFER_MIN_SIZE;6869private static final int LARGE_BUFFER_FREQUENCY = 100;7071private static final String FILE_NAME_PREFIX = "nio-out-file-";72private static final int VERBOSE_PERIOD = DEFAULT_ITERS / 10;7374private static final SplittableRandom SRAND = RandomFactory.getSplittableRandom();7576private static int iters = DEFAULT_ITERS;77private static int threadNum = DEFAULT_THREAD_NUM;7879private static BufferPoolMXBean getDirectPool() {80final List<BufferPoolMXBean> pools =81ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);82for (BufferPoolMXBean pool : pools) {83if (pool.getName().equals("direct")) {84return pool;85}86}87throw new Error("could not find direct pool");88}89private static final BufferPoolMXBean directPool = getDirectPool();90private static long initialCount;91private static long initialCapacity;9293// Each worker will do write operations on a file channel using94// buffers of various sizes. The buffer size is randomly chosen to95// be within a small or a large range. This way we can control96// which buffers can be cached (all, only the small ones, or none)97// by setting the jdk.nio.maxCachedBufferSize property.98private static class Worker implements Runnable {99private final int id;100private final CountDownLatch finishLatch, exitLatch;101private SplittableRandom random = SRAND.split();102private long smallBufferCount = 0;103private long largeBufferCount = 0;104105private int getWriteSize() {106int minSize = 0;107int diff = 0;108if (random.nextInt() % LARGE_BUFFER_FREQUENCY != 0) {109// small buffer110minSize = SMALL_BUFFER_MIN_SIZE;111diff = SMALL_BUFFER_DIFF_SIZE;112smallBufferCount += 1;113} else {114// large buffer115minSize = LARGE_BUFFER_MIN_SIZE;116diff = LARGE_BUFFER_DIFF_SIZE;117largeBufferCount += 1;118}119return minSize + random.nextInt(diff);120}121122private void loop() {123final String fileName = String.format("%s%d", FILE_NAME_PREFIX, id);124125try {126for (int i = 0; i < iters; i += 1) {127final int writeSize = getWriteSize();128129// This will allocate a HeapByteBuffer. It should not130// be a direct buffer, otherwise the write() method on131// the channel below will not create a temporary132// direct buffer for the write.133final ByteBuffer buffer = ByteBuffer.allocate(writeSize);134135// Put some random data on it.136while (buffer.hasRemaining()) {137buffer.put((byte) random.nextInt());138}139buffer.rewind();140141final Path file = Paths.get(fileName);142try (FileChannel outChannel = FileChannel.open(file, CREATE, TRUNCATE_EXISTING, WRITE)) {143// The write() method will create a temporary144// direct buffer for the write and attempt to cache145// it. It's important that buffer is not a146// direct buffer, otherwise the temporary buffer147// will not be created.148long res = outChannel.write(buffer);149}150151if ((i + 1) % VERBOSE_PERIOD == 0) {152System.out.printf(153" Worker %3d | %8d Iters | Small %8d Large %8d | Direct %4d / %7dK\n",154id, i + 1, smallBufferCount, largeBufferCount,155directPool.getCount(), directPool.getTotalCapacity() / 1024);156}157}158} catch (IOException e) {159throw new Error("I/O error", e);160} finally {161finishLatch.countDown();162try {163exitLatch.await();164} catch (InterruptedException e) {165// ignore166}167}168}169170@Override171public void run() {172loop();173}174175public Worker(int id, CountDownLatch finishLatch, CountDownLatch exitLatch) {176this.id = id;177this.finishLatch = finishLatch;178this.exitLatch = exitLatch;179}180}181182public static void checkDirectBuffers(long expectedCount, long expectedMax) {183final long directCount = directPool.getCount() - initialCount;184final long directTotalCapacity =185directPool.getTotalCapacity() - initialCapacity;186System.out.printf("Direct %d / %dK\n",187directCount, directTotalCapacity / 1024);188189if (directCount > expectedCount) {190throw new Error(String.format(191"inconsistent direct buffer total count, expected = %d, found = %d",192expectedCount, directCount));193}194195if (directTotalCapacity > expectedMax) {196throw new Error(String.format(197"inconsistent direct buffer total capacity, expected max = %d, found = %d",198expectedMax, directTotalCapacity));199}200}201202public static void main(String[] args) {203initialCount = directPool.getCount();204initialCapacity = directPool.getTotalCapacity();205206final String maxBufferSizeStr = System.getProperty("jdk.nio.maxCachedBufferSize");207final long maxBufferSize =208(maxBufferSizeStr != null) ? Long.valueOf(maxBufferSizeStr) : Long.MAX_VALUE;209210// We assume that the max cannot be equal to a size of a211// buffer that can be allocated (makes sanity checking at the212// end easier).213if ((SMALL_BUFFER_MIN_SIZE <= maxBufferSize &&214maxBufferSize <= SMALL_BUFFER_MAX_SIZE) ||215(LARGE_BUFFER_MIN_SIZE <= maxBufferSize &&216maxBufferSize <= LARGE_BUFFER_MAX_SIZE)) {217throw new Error(String.format("max buffer size = %d not allowed",218maxBufferSize));219}220221System.out.printf("Threads %d | Iterations %d | MaxBufferSize %d\n",222threadNum, iters, maxBufferSize);223System.out.println();224225final CountDownLatch finishLatch = new CountDownLatch(threadNum);226final CountDownLatch exitLatch = new CountDownLatch(1);227final Thread[] threads = new Thread[threadNum];228for (int i = 0; i < threadNum; i += 1) {229threads[i] = new Thread(new Worker(i, finishLatch, exitLatch));230threads[i].start();231}232233try {234try {235finishLatch.await();236} catch (InterruptedException e) {237throw new Error("finishLatch.await() interrupted!", e);238}239240// There is an assumption here that, at this point, only the241// cached DirectByteBuffers should be active. Given we242// haven't used any other DirectByteBuffers in this test, this243// should hold.244//245// Also note that we can only do the sanity checking at the246// end and not during the run given that, at any time, there247// could be buffers currently in use by some of the workers248// that will not be cached.249250System.out.println();251if (maxBufferSize < SMALL_BUFFER_MAX_SIZE) {252// The max buffer size is smaller than all buffers that253// were allocated. No buffers should have been cached.254checkDirectBuffers(0, 0);255} else if (maxBufferSize < LARGE_BUFFER_MIN_SIZE) {256// The max buffer size is larger than all small buffers257// but smaller than all large buffers that were258// allocated. Only small buffers could have been cached.259checkDirectBuffers(threadNum,260(long) threadNum * (long) SMALL_BUFFER_MAX_SIZE);261} else {262// The max buffer size is larger than all buffers that263// were allocated. All buffers could have been cached.264checkDirectBuffers(threadNum,265(long) threadNum * (long) LARGE_BUFFER_MAX_SIZE);266}267} finally {268exitLatch.countDown();269try {270for (int i = 0; i < threadNum; i += 1) {271threads[i].join();272}273} catch (InterruptedException e) {274// ignore275}276}277}278}279280281