739 lines
22 KiB
Java
739 lines
22 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2014 The Android Open Source Project
|
||
|
* Copyright (c) 2000, 2021, 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.security.AccessController;
|
||
|
|
||
|
import sun.misc.VM;
|
||
|
import jdk.internal.access.SharedSecrets;
|
||
|
import jdk.internal.misc.Unsafe;
|
||
|
|
||
|
import java.util.concurrent.atomic.AtomicLong;
|
||
|
|
||
|
/**
|
||
|
* 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);
|
||
|
}
|
||
|
|
||
|
// BEGIN Android-added: Getter / Setters needed due to unsupported ScopedMemoryAccess.
|
||
|
// -- get/put char --
|
||
|
|
||
|
static private 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 --
|
||
|
|
||
|
static private 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 --
|
||
|
|
||
|
static private 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 --
|
||
|
|
||
|
static private 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);
|
||
|
}
|
||
|
|
||
|
private static byte _get(long a) {
|
||
|
return UNSAFE.getByte(a);
|
||
|
}
|
||
|
|
||
|
private static void _put(long a, byte b) {
|
||
|
UNSAFE.putByte(a, b);
|
||
|
}
|
||
|
// END Android-added: Getter / Setters needed due to unsupported ScopedMemoryAccess.
|
||
|
|
||
|
// -- Unsafe access --
|
||
|
|
||
|
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||
|
|
||
|
// -- Processor and memory-system properties --
|
||
|
|
||
|
private static int PAGE_SIZE = -1;
|
||
|
|
||
|
static int pageSize() {
|
||
|
if (PAGE_SIZE == -1)
|
||
|
PAGE_SIZE = UNSAFE.pageSize();
|
||
|
return PAGE_SIZE;
|
||
|
}
|
||
|
|
||
|
static long pageCount(long size) {
|
||
|
return (size + (long)pageSize() - 1L) / pageSize();
|
||
|
}
|
||
|
// Android-removed: Remove unused methods.
|
||
|
/*
|
||
|
private static boolean UNALIGNED = UNSAFE.unalignedAccess();
|
||
|
|
||
|
static boolean unaligned() {
|
||
|
return UNALIGNED;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
|
||
|
// -- Direct memory management --
|
||
|
|
||
|
// BEGIN Android-removed: Direct memory management unused on Android.
|
||
|
/*
|
||
|
// 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=<size>".
|
||
|
private static volatile long MAX_MEMORY = VM.maxDirectMemory();
|
||
|
private static final AtomicLong RESERVED_MEMORY = new AtomicLong();
|
||
|
private static final AtomicLong TOTAL_CAPACITY = new AtomicLong();
|
||
|
private static final AtomicLong COUNT = new AtomicLong();
|
||
|
private static volatile boolean MEMORY_LIMIT_SET;
|
||
|
|
||
|
// max. number of sleeps during try-reserving with exponentially
|
||
|
// increasing delay before throwing OutOfMemoryError:
|
||
|
// 1, 2, 4, 8, 16, 32, 64, 128, 256 (total 511 ms ~ 0.5 s)
|
||
|
// which means that OOME will be thrown after 0.5 s of trying
|
||
|
private static final int MAX_SLEEPS = 9;
|
||
|
|
||
|
// 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, long cap) {
|
||
|
|
||
|
if (!MEMORY_LIMIT_SET && VM.initLevel() >= 1) {
|
||
|
MAX_MEMORY = VM.maxDirectMemory();
|
||
|
MEMORY_LIMIT_SET = true;
|
||
|
}
|
||
|
|
||
|
// optimist!
|
||
|
if (tryReserveMemory(size, cap)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();
|
||
|
boolean interrupted = false;
|
||
|
try {
|
||
|
|
||
|
// Retry allocation until success or there are no more
|
||
|
// references (including Cleaners that might free direct
|
||
|
// buffer memory) to process and allocation still fails.
|
||
|
boolean refprocActive;
|
||
|
do {
|
||
|
try {
|
||
|
refprocActive = jlra.waitForReferenceProcessing();
|
||
|
} catch (InterruptedException e) {
|
||
|
// Defer interrupts and keep trying.
|
||
|
interrupted = true;
|
||
|
refprocActive = true;
|
||
|
}
|
||
|
if (tryReserveMemory(size, cap)) {
|
||
|
return;
|
||
|
}
|
||
|
} while (refprocActive);
|
||
|
|
||
|
// trigger VM's Reference processing
|
||
|
System.gc();
|
||
|
|
||
|
// A retry loop with exponential back-off delays.
|
||
|
// Sometimes it would suffice to give up once reference
|
||
|
// processing is complete. But if there are many threads
|
||
|
// competing for memory, this gives more opportunities for
|
||
|
// any given thread to make progress. In particular, this
|
||
|
// seems to be enough for a stress test like
|
||
|
// DirectBufferAllocTest to (usually) succeed, while
|
||
|
// without it that test likely fails. Since failure here
|
||
|
// ends in OOME, there's no need to hurry.
|
||
|
long sleepTime = 1;
|
||
|
int sleeps = 0;
|
||
|
while (true) {
|
||
|
if (tryReserveMemory(size, cap)) {
|
||
|
return;
|
||
|
}
|
||
|
if (sleeps >= MAX_SLEEPS) {
|
||
|
break;
|
||
|
}
|
||
|
try {
|
||
|
if (!jlra.waitForReferenceProcessing()) {
|
||
|
Thread.sleep(sleepTime);
|
||
|
sleepTime <<= 1;
|
||
|
sleeps++;
|
||
|
}
|
||
|
} catch (InterruptedException e) {
|
||
|
interrupted = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// no luck
|
||
|
throw new OutOfMemoryError
|
||
|
("Cannot reserve "
|
||
|
+ size + " bytes of direct buffer memory (allocated: "
|
||
|
+ RESERVED_MEMORY.get() + ", limit: " + MAX_MEMORY +")");
|
||
|
|
||
|
} finally {
|
||
|
if (interrupted) {
|
||
|
// don't swallow interrupts
|
||
|
Thread.currentThread().interrupt();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static boolean tryReserveMemory(long size, long 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 <= MAX_MEMORY - (totalCap = TOTAL_CAPACITY.get())) {
|
||
|
if (TOTAL_CAPACITY.compareAndSet(totalCap, totalCap + cap)) {
|
||
|
RESERVED_MEMORY.addAndGet(size);
|
||
|
COUNT.incrementAndGet();
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void unreserveMemory(long size, long cap) {
|
||
|
long cnt = COUNT.decrementAndGet();
|
||
|
long reservedMem = RESERVED_MEMORY.addAndGet(-size);
|
||
|
long totalCap = TOTAL_CAPACITY.addAndGet(-cap);
|
||
|
assert cnt >= 0 && reservedMem >= 0 && totalCap >= 0;
|
||
|
}
|
||
|
|
||
|
static final BufferPool BUFFER_POOL = new BufferPool() {
|
||
|
@Override
|
||
|
public String getName() {
|
||
|
return "direct";
|
||
|
}
|
||
|
@Override
|
||
|
public long getCount() {
|
||
|
return Bits.COUNT.get();
|
||
|
}
|
||
|
@Override
|
||
|
public long getTotalCapacity() {
|
||
|
return Bits.TOTAL_CAPACITY.get();
|
||
|
}
|
||
|
@Override
|
||
|
public long getMemoryUsed() {
|
||
|
return Bits.RESERVED_MEMORY.get();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// 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;
|
||
|
*/
|
||
|
// END Android-removed: Direct memory management unused on Android.
|
||
|
}
|