diff --git a/src/java.base/share/classes/java/nio/Bits.java b/src/java.base/share/classes/java/nio/Bits.java index b11cb4947db..a9a22d6079e 100644 --- a/src/java.base/share/classes/java/nio/Bits.java +++ b/src/java.base/share/classes/java/nio/Bits.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, 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 @@ -234,4 +234,28 @@ public long getMemoryUsed() { // of an element by element copy. These numbers may change over time. static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6; static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6; + + // Maximum number of bytes to set in one call to {@code Unsafe.setMemory}. + // This threshold allows safepoint polling during large memory operations. + static final long UNSAFE_SET_THRESHOLD = 1024 * 1024; + + /** + * Sets a block of memory starting from a given address to a specified byte value. + * + * @param srcAddr + * the starting memory address + * @param count + * the number of bytes to set + * @param value + * the byte value to set + */ + static void setMemory(long srcAddr, long count, byte value) { + long offset = 0; + while (offset < count) { + long len = Math.min(UNSAFE_SET_THRESHOLD, count - offset); + UNSAFE.setMemory(srcAddr + offset, len, value); + offset += len; + } + } + } diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template index e8551bd78c6..f3f0e08b8c9 100644 --- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2025, 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 @@ -117,7 +117,7 @@ class Direct$Type$Buffer$RW$$BO$ Bits.unreserveMemory(size, cap); throw x; } - UNSAFE.setMemory(base, size, (byte) 0); + Bits.setMemory(base, size, (byte) 0); if (pa && (base % ps != 0)) { // Round up to page boundary address = base + ps - (base & (ps - 1)); diff --git a/test/jdk/java/nio/Buffer/AllocateDirectInit.java b/test/jdk/java/nio/Buffer/AllocateDirectInit.java index c54536df8ba..472187fdcda 100644 --- a/test/jdk/java/nio/Buffer/AllocateDirectInit.java +++ b/test/jdk/java/nio/Buffer/AllocateDirectInit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2025, 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 @@ -23,29 +23,60 @@ /** * @test - * @bug 4490253 6535542 + * @bug 4490253 6535542 8357959 + * @key randomness + * @library /test/lib + * @build jdk.test.lib.RandomFactory * @summary Verify that newly allocated direct buffers are initialized. + * @run main/othervm AllocateDirectInit */ import java.nio.ByteBuffer; +import java.util.Random; + +import jdk.test.lib.RandomFactory; public class AllocateDirectInit { + private static final int MAX_BIN_LIMIT = 16 * 1024 * 1024; + private static final int MAX_DEC_LIMIT = 10 * 1000 * 1000; + private static final int TRIES_PER_LIMIT = 1024; + + private static final Random RND = RandomFactory.getRandom(); + public static void main(String [] args){ - for (int i = 0; i < 1024; i++) { - ByteBuffer bb = ByteBuffer.allocateDirect(1024); -// printByteBuffer(bb); - for (bb.position(0); bb.position() < bb.limit(); ) { - if ((bb.get() & 0xff) != 0) - throw new RuntimeException("uninitialized buffer, position = " - + bb.position()); + // Try power of two limits + for (int limit = 1; limit < MAX_BIN_LIMIT; limit *= 2) { + check(ByteBuffer.allocateDirect(limit - 1)); + check(ByteBuffer.allocateDirect(limit)); + check(ByteBuffer.allocateDirect(limit + 1)); + } + + // Try power of ten limits + for (int limit = 1; limit < MAX_DEC_LIMIT; limit *= 10) { + check(ByteBuffer.allocateDirect(limit - 1)); + check(ByteBuffer.allocateDirect(limit)); + check(ByteBuffer.allocateDirect(limit + 1)); + } + + // Try random sizes within power of two limits + for (int limit = 1; limit < MAX_BIN_LIMIT; limit *= 2) { + for (int t = 0; t < TRIES_PER_LIMIT; t++) { + check(ByteBuffer.allocateDirect(RND.nextInt(limit))); } } } - private static void printByteBuffer(ByteBuffer bb) { - System.out.print("byte ["); - for (bb.position(0); bb.position() < bb.limit(); ) - System.out.print(" " + Integer.toHexString(bb.get() & 0xff)); - System.out.println(" ]"); + private static void check(ByteBuffer bb) { + while (bb.hasRemaining()) { + if (bb.get() != 0) { + int mismatchPos = bb.position(); + System.out.print("byte ["); + for (bb.position(0); bb.position() < bb.limit(); ) { + System.out.print(" " + Integer.toHexString(bb.get() & 0xff)); + } + System.out.println(" ]"); + throw new RuntimeException("uninitialized buffer, position = " + mismatchPos); + } + } } } diff --git a/test/micro/org/openjdk/bench/java/nio/DirectByteBufferAlloc.java b/test/micro/org/openjdk/bench/java/nio/DirectByteBufferAlloc.java new file mode 100644 index 00000000000..8e82b9a4487 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/nio/DirectByteBufferAlloc.java @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com Inc. 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. + * + */ +package org.openjdk.bench.java.nio; + + +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(3) +public class DirectByteBufferAlloc { + + @Param({ + "128", // 128 bytes + "1024", // 1KB + "1048576", // 1 MB + "16777216" // 16MB + }) + public int bytes; + + @Benchmark + public ByteBuffer allocateDirectBuffer() { + return ByteBuffer.allocateDirect(bytes); + } +}