/* * Copyright (c) 2000, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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 java.nio; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.StampedLock; import jdk.internal.misc.JavaNioAccess; import jdk.internal.misc.JavaLangRefAccess; import jdk.internal.misc.SharedSecrets; import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM; import jdk.internal.ref.CleanerFactory; /** * Access to bits, native and otherwise. */ class Bits { // package-private private Bits() { } // -- Swapping -- static short swap(short x) { return Short.reverseBytes(x); } static char swap(char x) { return Character.reverseBytes(x); } static int swap(int x) { return Integer.reverseBytes(x); } static long swap(long x) { return Long.reverseBytes(x); } // -- get/put char -- private static char makeChar(byte b1, byte b0) { return (char)((b1 << 8) | (b0 & 0xff)); } static char getCharL(ByteBuffer bb, int bi) { return makeChar(bb._get(bi + 1), bb._get(bi )); } static char getCharL(long a) { return makeChar(_get(a + 1), _get(a )); } static char getCharB(ByteBuffer bb, int bi) { return makeChar(bb._get(bi ), bb._get(bi + 1)); } static char getCharB(long a) { return makeChar(_get(a ), _get(a + 1)); } static char getChar(ByteBuffer bb, int bi, boolean bigEndian) { return bigEndian ? getCharB(bb, bi) : getCharL(bb, bi); } static char getChar(long a, boolean bigEndian) { return bigEndian ? getCharB(a) : getCharL(a); } private static byte char1(char x) { return (byte)(x >> 8); } private static byte char0(char x) { return (byte)(x ); } static void putCharL(ByteBuffer bb, int bi, char x) { bb._put(bi , char0(x)); bb._put(bi + 1, char1(x)); } static void putCharL(long a, char x) { _put(a , char0(x)); _put(a + 1, char1(x)); } static void putCharB(ByteBuffer bb, int bi, char x) { bb._put(bi , char1(x)); bb._put(bi + 1, char0(x)); } static void putCharB(long a, char x) { _put(a , char1(x)); _put(a + 1, char0(x)); } static void putChar(ByteBuffer bb, int bi, char x, boolean bigEndian) { if (bigEndian) putCharB(bb, bi, x); else putCharL(bb, bi, x); } static void putChar(long a, char x, boolean bigEndian) { if (bigEndian) putCharB(a, x); else putCharL(a, x); } // -- get/put short -- private static short makeShort(byte b1, byte b0) { return (short)((b1 << 8) | (b0 & 0xff)); } static short getShortL(ByteBuffer bb, int bi) { return makeShort(bb._get(bi + 1), bb._get(bi )); } static short getShortL(long a) { return makeShort(_get(a + 1), _get(a )); } static short getShortB(ByteBuffer bb, int bi) { return makeShort(bb._get(bi ), bb._get(bi + 1)); } static short getShortB(long a) { return makeShort(_get(a ), _get(a + 1)); } static short getShort(ByteBuffer bb, int bi, boolean bigEndian) { return bigEndian ? getShortB(bb, bi) : getShortL(bb, bi); } static short getShort(long a, boolean bigEndian) { return bigEndian ? getShortB(a) : getShortL(a); } private static byte short1(short x) { return (byte)(x >> 8); } private static byte short0(short x) { return (byte)(x ); } static void putShortL(ByteBuffer bb, int bi, short x) { bb._put(bi , short0(x)); bb._put(bi + 1, short1(x)); } static void putShortL(long a, short x) { _put(a , short0(x)); _put(a + 1, short1(x)); } static void putShortB(ByteBuffer bb, int bi, short x) { bb._put(bi , short1(x)); bb._put(bi + 1, short0(x)); } static void putShortB(long a, short x) { _put(a , short1(x)); _put(a + 1, short0(x)); } static void putShort(ByteBuffer bb, int bi, short x, boolean bigEndian) { if (bigEndian) putShortB(bb, bi, x); else putShortL(bb, bi, x); } static void putShort(long a, short x, boolean bigEndian) { if (bigEndian) putShortB(a, x); else putShortL(a, x); } // -- get/put int -- private static int makeInt(byte b3, byte b2, byte b1, byte b0) { return (((b3 ) << 24) | ((b2 & 0xff) << 16) | ((b1 & 0xff) << 8) | ((b0 & 0xff) )); } static int getIntL(ByteBuffer bb, int bi) { return makeInt(bb._get(bi + 3), bb._get(bi + 2), bb._get(bi + 1), bb._get(bi )); } static int getIntL(long a) { return makeInt(_get(a + 3), _get(a + 2), _get(a + 1), _get(a )); } static int getIntB(ByteBuffer bb, int bi) { return makeInt(bb._get(bi ), bb._get(bi + 1), bb._get(bi + 2), bb._get(bi + 3)); } static int getIntB(long a) { return makeInt(_get(a ), _get(a + 1), _get(a + 2), _get(a + 3)); } static int getInt(ByteBuffer bb, int bi, boolean bigEndian) { return bigEndian ? getIntB(bb, bi) : getIntL(bb, bi) ; } static int getInt(long a, boolean bigEndian) { return bigEndian ? getIntB(a) : getIntL(a) ; } private static byte int3(int x) { return (byte)(x >> 24); } private static byte int2(int x) { return (byte)(x >> 16); } private static byte int1(int x) { return (byte)(x >> 8); } private static byte int0(int x) { return (byte)(x ); } static void putIntL(ByteBuffer bb, int bi, int x) { bb._put(bi + 3, int3(x)); bb._put(bi + 2, int2(x)); bb._put(bi + 1, int1(x)); bb._put(bi , int0(x)); } static void putIntL(long a, int x) { _put(a + 3, int3(x)); _put(a + 2, int2(x)); _put(a + 1, int1(x)); _put(a , int0(x)); } static void putIntB(ByteBuffer bb, int bi, int x) { bb._put(bi , int3(x)); bb._put(bi + 1, int2(x)); bb._put(bi + 2, int1(x)); bb._put(bi + 3, int0(x)); } static void putIntB(long a, int x) { _put(a , int3(x)); _put(a + 1, int2(x)); _put(a + 2, int1(x)); _put(a + 3, int0(x)); } static void putInt(ByteBuffer bb, int bi, int x, boolean bigEndian) { if (bigEndian) putIntB(bb, bi, x); else putIntL(bb, bi, x); } static void putInt(long a, int x, boolean bigEndian) { if (bigEndian) putIntB(a, x); else putIntL(a, x); } // -- get/put long -- private static long makeLong(byte b7, byte b6, byte b5, byte b4, byte b3, byte b2, byte b1, byte b0) { return ((((long)b7 ) << 56) | (((long)b6 & 0xff) << 48) | (((long)b5 & 0xff) << 40) | (((long)b4 & 0xff) << 32) | (((long)b3 & 0xff) << 24) | (((long)b2 & 0xff) << 16) | (((long)b1 & 0xff) << 8) | (((long)b0 & 0xff) )); } static long getLongL(ByteBuffer bb, int bi) { return makeLong(bb._get(bi + 7), bb._get(bi + 6), bb._get(bi + 5), bb._get(bi + 4), bb._get(bi + 3), bb._get(bi + 2), bb._get(bi + 1), bb._get(bi )); } static long getLongL(long a) { return makeLong(_get(a + 7), _get(a + 6), _get(a + 5), _get(a + 4), _get(a + 3), _get(a + 2), _get(a + 1), _get(a )); } static long getLongB(ByteBuffer bb, int bi) { return makeLong(bb._get(bi ), bb._get(bi + 1), bb._get(bi + 2), bb._get(bi + 3), bb._get(bi + 4), bb._get(bi + 5), bb._get(bi + 6), bb._get(bi + 7)); } static long getLongB(long a) { return makeLong(_get(a ), _get(a + 1), _get(a + 2), _get(a + 3), _get(a + 4), _get(a + 5), _get(a + 6), _get(a + 7)); } static long getLong(ByteBuffer bb, int bi, boolean bigEndian) { return bigEndian ? getLongB(bb, bi) : getLongL(bb, bi); } static long getLong(long a, boolean bigEndian) { return bigEndian ? getLongB(a) : getLongL(a); } private static byte long7(long x) { return (byte)(x >> 56); } private static byte long6(long x) { return (byte)(x >> 48); } private static byte long5(long x) { return (byte)(x >> 40); } private static byte long4(long x) { return (byte)(x >> 32); } private static byte long3(long x) { return (byte)(x >> 24); } private static byte long2(long x) { return (byte)(x >> 16); } private static byte long1(long x) { return (byte)(x >> 8); } private static byte long0(long x) { return (byte)(x ); } static void putLongL(ByteBuffer bb, int bi, long x) { bb._put(bi + 7, long7(x)); bb._put(bi + 6, long6(x)); bb._put(bi + 5, long5(x)); bb._put(bi + 4, long4(x)); bb._put(bi + 3, long3(x)); bb._put(bi + 2, long2(x)); bb._put(bi + 1, long1(x)); bb._put(bi , long0(x)); } static void putLongL(long a, long x) { _put(a + 7, long7(x)); _put(a + 6, long6(x)); _put(a + 5, long5(x)); _put(a + 4, long4(x)); _put(a + 3, long3(x)); _put(a + 2, long2(x)); _put(a + 1, long1(x)); _put(a , long0(x)); } static void putLongB(ByteBuffer bb, int bi, long x) { bb._put(bi , long7(x)); bb._put(bi + 1, long6(x)); bb._put(bi + 2, long5(x)); bb._put(bi + 3, long4(x)); bb._put(bi + 4, long3(x)); bb._put(bi + 5, long2(x)); bb._put(bi + 6, long1(x)); bb._put(bi + 7, long0(x)); } static void putLongB(long a, long x) { _put(a , long7(x)); _put(a + 1, long6(x)); _put(a + 2, long5(x)); _put(a + 3, long4(x)); _put(a + 4, long3(x)); _put(a + 5, long2(x)); _put(a + 6, long1(x)); _put(a + 7, long0(x)); } static void putLong(ByteBuffer bb, int bi, long x, boolean bigEndian) { if (bigEndian) putLongB(bb, bi, x); else putLongL(bb, bi, x); } static void putLong(long a, long x, boolean bigEndian) { if (bigEndian) putLongB(a, x); else putLongL(a, x); } // -- get/put float -- static float getFloatL(ByteBuffer bb, int bi) { return Float.intBitsToFloat(getIntL(bb, bi)); } static float getFloatL(long a) { return Float.intBitsToFloat(getIntL(a)); } static float getFloatB(ByteBuffer bb, int bi) { return Float.intBitsToFloat(getIntB(bb, bi)); } static float getFloatB(long a) { return Float.intBitsToFloat(getIntB(a)); } static float getFloat(ByteBuffer bb, int bi, boolean bigEndian) { return bigEndian ? getFloatB(bb, bi) : getFloatL(bb, bi); } static float getFloat(long a, boolean bigEndian) { return bigEndian ? getFloatB(a) : getFloatL(a); } static void putFloatL(ByteBuffer bb, int bi, float x) { putIntL(bb, bi, Float.floatToRawIntBits(x)); } static void putFloatL(long a, float x) { putIntL(a, Float.floatToRawIntBits(x)); } static void putFloatB(ByteBuffer bb, int bi, float x) { putIntB(bb, bi, Float.floatToRawIntBits(x)); } static void putFloatB(long a, float x) { putIntB(a, Float.floatToRawIntBits(x)); } static void putFloat(ByteBuffer bb, int bi, float x, boolean bigEndian) { if (bigEndian) putFloatB(bb, bi, x); else putFloatL(bb, bi, x); } static void putFloat(long a, float x, boolean bigEndian) { if (bigEndian) putFloatB(a, x); else putFloatL(a, x); } // -- get/put double -- static double getDoubleL(ByteBuffer bb, int bi) { return Double.longBitsToDouble(getLongL(bb, bi)); } static double getDoubleL(long a) { return Double.longBitsToDouble(getLongL(a)); } static double getDoubleB(ByteBuffer bb, int bi) { return Double.longBitsToDouble(getLongB(bb, bi)); } static double getDoubleB(long a) { return Double.longBitsToDouble(getLongB(a)); } static double getDouble(ByteBuffer bb, int bi, boolean bigEndian) { return bigEndian ? getDoubleB(bb, bi) : getDoubleL(bb, bi); } static double getDouble(long a, boolean bigEndian) { return bigEndian ? getDoubleB(a) : getDoubleL(a); } static void putDoubleL(ByteBuffer bb, int bi, double x) { putLongL(bb, bi, Double.doubleToRawLongBits(x)); } static void putDoubleL(long a, double x) { putLongL(a, Double.doubleToRawLongBits(x)); } static void putDoubleB(ByteBuffer bb, int bi, double x) { putLongB(bb, bi, Double.doubleToRawLongBits(x)); } static void putDoubleB(long a, double x) { putLongB(a, Double.doubleToRawLongBits(x)); } static void putDouble(ByteBuffer bb, int bi, double x, boolean bigEndian) { if (bigEndian) putDoubleB(bb, bi, x); else putDoubleL(bb, bi, x); } static void putDouble(long a, double x, boolean bigEndian) { if (bigEndian) putDoubleB(a, x); else putDoubleL(a, x); } // -- Unsafe access -- private static final Unsafe unsafe = Unsafe.getUnsafe(); private static byte _get(long a) { return unsafe.getByte(a); } private static void _put(long a, byte b) { unsafe.putByte(a, b); } static Unsafe unsafe() { return unsafe; } // -- Processor and memory-system properties -- private static final ByteOrder byteOrder = unsafe.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN; static ByteOrder byteOrder() { return byteOrder; } private static int pageSize = -1; static int pageSize() { if (pageSize == -1) pageSize = unsafe().pageSize(); return pageSize; } static int pageCount(long size) { return (int)(size + (long)pageSize() - 1L) / pageSize(); } private static boolean unaligned = unsafe.unalignedAccess(); static boolean unaligned() { return unaligned; } // -- Direct memory management -- // A user-settable upper limit on the maximum amount of allocatable // direct buffer memory. This value may be changed during VM // initialization if it is launched with "-XX:MaxDirectMemorySize=". // A change to maxMemory is published via memoryLimitSet volatile boolean // flag changing state from false -> true, so maxMemory need not be volatile. private static long maxMemory = VM.maxDirectMemory(); private static volatile boolean memoryLimitSet; private static final AtomicLong reservedMemory = new AtomicLong(); private static final AtomicLong totalCapacity = new AtomicLong(); private static final AtomicLong count = new AtomicLong(); // A fair lock for direct memory allocator threads to queue after 1st optimistic // reservation fails so that only a single thread at a time is retrying // reservation while: // - waiting for unreservation events until time out // - followed by triggering reference discovery // - followed by another round of waiting for unreservation events until time out // ... before finally giving up with OutOfMemoryError. private static final StampedLock reserveLock = new StampedLock(); // A counter of unreservations incremented by unreservation threads, // guarded by unreserveLock, but also read by reservation threads without // holding unreserveLock, so it must be volatile. private static volatile int unreserveCount; // Last value of 'unreserveCount' observed by reservation threads, // guarded by reserveLock. If 'lastUnreserveCount' and 'unreserveCount' // differ, an unreservation event (or multiple events) is detected. private static int lastUnreserveCount; // the System.nanoTime() of the detection of last unreservation event // by the reservation thread(s), guarded by reserveLock. private static long lastUnreserveNanoTime; // flag indicating if 'lastUnreserveNanoTime' has already been set, // guarded by reserveLock. private static boolean lastUnreserveNanoTimeSet; // max. wait time for unreservation event before triggering GC and/or // failing with OOME. private static final long MAX_UNRESERVE_WAIT_NANOS = TimeUnit.SECONDS.toNanos(1L); // the duration of last successful waiting for unreservation // event by reservation thread. starts with MAX_UNRESERVE_WAIT_NANOS / 4 // and is dynamically adjusted at each successful call to awaitUnreserveMemory() // that actually waited for the event. private static long lastSuccessfullUnreserveWaitNanos = MAX_UNRESERVE_WAIT_NANOS / 4; // monitor lock via which unreservation threads notify reservation threads // about the unreservation events. private static final Object unreserveLock = new Object(); // These methods should be called whenever direct memory is allocated or // freed. They allow the user to control the amount of direct memory // which a process may access. All sizes are specified in bytes. static void reserveMemory(long size, int cap) { if (!memoryLimitSet && VM.initLevel() >= 1) { maxMemory = VM.maxDirectMemory(); memoryLimitSet = true; } JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess(); // optimist! long stamp = reserveLock.tryReadLock(); if (stamp != 0L) try { // retry reservation while helping the Cleaner thread process enqueued // Cleanable(s) which includes the ones that free direct buffer memory // until the queue drains out do { if (tryReserveMemory(size, cap)) { return; } } while (jlra.cleanNextEnqueuedCleanable(CleanerFactory.cleaner())); } finally { reserveLock.unlockRead(stamp); } // reservation threads that don't succeed at first must queue so that // only one of them at a time is triggering reference discovery and // retrials with waiting on un-reservation events... stamp = reserveLock.writeLock(); try { // retry reservation until there are no unreservation events // detected for at least 4 times as much as we waited for last // successful unreservation event but no more than // MAX_UNRESERVE_WAIT_NANOS. do { if (tryReserveMemory(size, cap)) { return; } } while (awaitUnreserveMemory(Math.min(MAX_UNRESERVE_WAIT_NANOS, lastSuccessfullUnreserveWaitNanos * 4))); // trigger reference discovery System.gc(); // GC stops the world and can last for a long time, // so restart timing from 0. lastUnreserveNanoTime = System.nanoTime(); // retry reservation until there are no unreservation events // detected for at least MAX_UNRESERVE_WAIT_NANOS. do { if (tryReserveMemory(size, cap)) { return; } } while (awaitUnreserveMemory(MAX_UNRESERVE_WAIT_NANOS)); // no luck throw new OutOfMemoryError("Direct buffer memory"); } finally { reserveLock.unlockWrite(stamp); } } private static boolean tryReserveMemory(long size, int cap) { // -XX:MaxDirectMemorySize limits the total capacity rather than the // actual memory usage, which will differ when buffers are page // aligned. long totalCap; while (cap <= maxMemory - (totalCap = totalCapacity.get())) { if (totalCapacity.compareAndSet(totalCap, totalCap + cap)) { reservedMemory.addAndGet(size); count.incrementAndGet(); return true; } } return false; } private static boolean awaitUnreserveMemory(long timeoutNanos) { assert reserveLock.isWriteLocked(); // optimistic 1st try without lock int c; if ((c = unreserveCount) != lastUnreserveCount) { lastUnreserveCount = c; lastUnreserveNanoTime = System.nanoTime(); lastUnreserveNanoTimeSet = true; return true; } boolean interrupted = false; try { synchronized (unreserveLock) { long nt = System.nanoTime(); if (!lastUnreserveNanoTimeSet) { lastUnreserveNanoTime = nt; lastUnreserveNanoTimeSet = true; } long deadline = lastUnreserveNanoTime + timeoutNanos; long waitUnreserve; while ((c = unreserveCount) == lastUnreserveCount && (waitUnreserve = deadline - nt) > 0L) { interrupted |= waitNanos(unreserveLock, waitUnreserve); nt = System.nanoTime(); } if (c == lastUnreserveCount) { // time out return false; } else { lastSuccessfullUnreserveWaitNanos = nt - lastUnreserveNanoTime; lastUnreserveCount = c; lastUnreserveNanoTime = nt; return true; } } } finally { if (interrupted) { Thread.currentThread().interrupt(); } } } private static boolean waitNanos(Object lock, long waitNanos) { try { long millis = TimeUnit.NANOSECONDS.toMillis(waitNanos); int nanos = (int) (waitNanos - TimeUnit.MILLISECONDS.toNanos(millis)); lock.wait(millis, nanos); return false; } catch (InterruptedException e) { return true; } } static void unreserveMemory(long size, int cap) { synchronized (unreserveLock) { long cnt = count.decrementAndGet(); long reservedMem = reservedMemory.addAndGet(-size); long totalCap = totalCapacity.addAndGet(-cap); assert cnt >= 0 && reservedMem >= 0 && totalCap >= 0; unreserveCount++; unreserveLock.notifyAll(); } } // -- Monitoring of direct buffer usage -- static { // setup access to this package in SharedSecrets SharedSecrets.setJavaNioAccess( new JavaNioAccess() { @Override public JavaNioAccess.BufferPool getDirectBufferPool() { return new JavaNioAccess.BufferPool() { @Override public String getName() { return "direct"; } @Override public long getCount() { return Bits.count.get(); } @Override public long getTotalCapacity() { return Bits.totalCapacity.get(); } @Override public long getMemoryUsed() { return Bits.reservedMemory.get(); } }; } @Override public ByteBuffer newDirectByteBuffer(long addr, int cap, Object ob) { return new DirectByteBuffer(addr, cap, ob); } @Override public void truncate(Buffer buf) { buf.truncate(); } }); } // These numbers represent the point at which we have empirically // determined that the average cost of a JNI call exceeds the expense // 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; }