| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. You may obtain a |
| * copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package org.apache.geode.internal.offheap; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.nio.ByteBuffer; |
| |
| import org.apache.geode.annotations.Immutable; |
| import org.apache.geode.annotations.internal.MakeNotStatic; |
| import org.apache.geode.internal.JvmSizeUtils; |
| import org.apache.geode.unsafe.internal.sun.misc.Unsafe; |
| |
| /** |
| * This class supports allocating and freeing large amounts of addressable memory (i.e. slabs). It |
| * also supports using an "address" to operate on the memory. Note that this class's implementation |
| * is currently a singleton so all the methods on it are static. |
| */ |
| public class AddressableMemoryManager { |
| @Immutable |
| private static final Unsafe unsafe; |
| private static final int ARRAY_BYTE_BASE_OFFSET; |
| private static final String reason; |
| static { |
| Unsafe tmp = null; |
| String tmpReason = null; |
| try { |
| tmp = new Unsafe(); |
| } catch (RuntimeException ignore) { |
| tmpReason = ignore.toString(); |
| } catch (Error ignore) { |
| tmpReason = ignore.toString(); |
| } |
| reason = tmpReason; |
| unsafe = tmp; |
| ARRAY_BYTE_BASE_OFFSET = unsafe != null ? unsafe.arrayBaseOffset(byte[].class) : 0; |
| } |
| |
| public static long allocate(int size) { |
| if (unsafe == null) { |
| throw new OutOfMemoryError("Off-heap memory is not available because: " + reason); |
| } |
| try { |
| return unsafe.allocateMemory(size); |
| } catch (OutOfMemoryError err) { |
| String msg = "Failed creating " + size + " bytes of off-heap memory during cache creation."; |
| if (err.getMessage() != null && !err.getMessage().isEmpty()) { |
| msg += " Cause: " + err.getMessage(); |
| } |
| if (!JvmSizeUtils.is64Bit() && size >= (1024 * 1024 * 1024)) { |
| msg += |
| " The JVM looks like a 32-bit one. For large amounts of off-heap memory a 64-bit JVM is needed."; |
| } |
| throw new OutOfMemoryError(msg); |
| } |
| } |
| |
| public static void free(long addr) { |
| unsafe.freeMemory(addr); |
| } |
| |
| public static Slab allocateSlab(int size) { |
| return new SlabImpl(size); |
| } |
| |
| public static byte readByte(long addr) { |
| return unsafe.getByte(addr); |
| } |
| |
| public static char readChar(long addr) { |
| return unsafe.getChar(null, addr); |
| } |
| |
| public static short readShort(long addr) { |
| return unsafe.getShort(null, addr); |
| } |
| |
| public static int readInt(long addr) { |
| return unsafe.getInt(null, addr); |
| } |
| |
| public static int readIntVolatile(long addr) { |
| return unsafe.getIntVolatile(null, addr); |
| } |
| |
| public static long readLong(long addr) { |
| return unsafe.getLong(null, addr); |
| } |
| |
| public static long readLongVolatile(long addr) { |
| return unsafe.getLongVolatile(null, addr); |
| } |
| |
| public static void writeByte(long addr, byte value) { |
| unsafe.putByte(addr, value); |
| } |
| |
| public static void writeInt(long addr, int value) { |
| unsafe.putInt(null, addr, value); |
| } |
| |
| public static void writeIntVolatile(long addr, int value) { |
| unsafe.putIntVolatile(null, addr, value); |
| } |
| |
| public static boolean writeIntVolatile(long addr, int expected, int value) { |
| return unsafe.compareAndSwapInt(null, addr, expected, value); |
| } |
| |
| public static void writeLong(long addr, long value) { |
| unsafe.putLong(null, addr, value); |
| } |
| |
| public static void writeLongVolatile(long addr, long value) { |
| unsafe.putLongVolatile(null, addr, value); |
| } |
| |
| public static boolean writeLongVolatile(long addr, long expected, long value) { |
| return unsafe.compareAndSwapLong(null, addr, expected, value); |
| } |
| |
| public static void readBytes(long addr, byte[] bytes, int bytesOffset, int size) { |
| // Throwing an Error instead of using the "assert" keyword because passing < 0 to |
| // copyMemory(...) can lead to a core dump with some JVMs and we don't want to |
| // require the -ea JVM flag. |
| if (size < 0) { |
| throw new AssertionError("Size=" + size + ", but size must be >= 0"); |
| } |
| |
| assert bytesOffset >= 0 : "byteOffset=" + bytesOffset; |
| assert bytesOffset + size <= bytes.length : "byteOffset=" + bytesOffset + ",size=" + size |
| + ",bytes.length=" + bytes.length; |
| |
| if (size == 0) { |
| return; // No point in wasting time copying 0 bytes |
| } |
| unsafe.copyMemory(null, addr, bytes, ARRAY_BYTE_BASE_OFFSET + bytesOffset, size); |
| } |
| |
| public static void copyMemory(long srcAddr, long dstAddr, long size) { |
| unsafe.copyMemory(srcAddr, dstAddr, size); |
| } |
| |
| public static void writeBytes(long addr, byte[] bytes, int bytesOffset, int size) { |
| // Throwing an Error instead of using the "assert" keyword because passing < 0 to |
| // copyMemory(...) can lead to a core dump with some JVMs and we don't want to |
| // require the -ea JVM flag. |
| if (size < 0) { |
| throw new AssertionError("Size=" + size + ", but size must be >= 0"); |
| } |
| |
| assert bytesOffset >= 0 : "byteOffset=" + bytesOffset; |
| assert bytesOffset + size <= bytes.length : "byteOffset=" + bytesOffset + ",size=" + size |
| + ",bytes.length=" + bytes.length; |
| |
| if (size == 0) { |
| return; // No point in wasting time copying 0 bytes |
| } |
| unsafe.copyMemory(bytes, ARRAY_BYTE_BASE_OFFSET + bytesOffset, null, addr, size); |
| } |
| |
| public static void fill(long addr, int size, byte fill) { |
| unsafe.setMemory(addr, size, fill); |
| } |
| |
| @SuppressWarnings("rawtypes") |
| @MakeNotStatic |
| private static volatile Class dbbClass = null; |
| @SuppressWarnings("rawtypes") |
| @MakeNotStatic |
| private static volatile Constructor dbbCtor = null; |
| @MakeNotStatic |
| private static volatile boolean dbbCreateFailed = false; |
| @MakeNotStatic |
| private static volatile Method dbbAddressMethod = null; |
| @MakeNotStatic |
| private static volatile boolean dbbAddressFailed = false; |
| |
| /** |
| * Returns the address of the Unsafe memory for the first byte of a direct ByteBuffer. If the |
| * buffer is not direct or the address can not be obtained return 0. |
| */ |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public static long getDirectByteBufferAddress(ByteBuffer bb) { |
| if (!bb.isDirect()) { |
| return 0L; |
| } |
| if (dbbAddressFailed) { |
| return 0L; |
| } |
| Method m = dbbAddressMethod; |
| if (m == null) { |
| Class c = dbbClass; |
| if (c == null) { |
| try { |
| c = Class.forName("java.nio.DirectByteBuffer"); |
| } catch (ClassNotFoundException e) { |
| // throw new IllegalStateException("Could not find java.nio.DirectByteBuffer", e); |
| dbbCreateFailed = true; |
| dbbAddressFailed = true; |
| return 0L; |
| } |
| dbbClass = c; |
| } |
| try { |
| m = c.getDeclaredMethod("address"); |
| } catch (NoSuchMethodException | SecurityException e) { |
| // throw new IllegalStateException("Could not get method DirectByteBuffer.address()", e); |
| dbbClass = null; |
| dbbAddressFailed = true; |
| return 0L; |
| } |
| m.setAccessible(true); |
| dbbAddressMethod = m; |
| } |
| try { |
| return (Long) m.invoke(bb); |
| } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { |
| // throw new IllegalStateException("Could not create an invoke DirectByteBuffer.address()", |
| // e); |
| dbbClass = null; |
| dbbAddressMethod = null; |
| dbbAddressFailed = true; |
| return 0L; |
| } |
| } |
| |
| /** |
| * Create a direct byte buffer given its address and size. The returned ByteBuffer will be direct |
| * and use the memory at the given address. |
| * |
| * @return the created direct byte buffer or null if it could not be created. |
| */ |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| static ByteBuffer createDirectByteBuffer(long address, int size) { |
| if (dbbCreateFailed) { |
| return null; |
| } |
| Constructor ctor = dbbCtor; |
| if (ctor == null) { |
| Class c = dbbClass; |
| if (c == null) { |
| try { |
| c = Class.forName("java.nio.DirectByteBuffer"); |
| } catch (ClassNotFoundException e) { |
| // throw new IllegalStateException("Could not find java.nio.DirectByteBuffer", e); |
| dbbCreateFailed = true; |
| dbbAddressFailed = true; |
| return null; |
| } |
| dbbClass = c; |
| } |
| try { |
| ctor = c.getDeclaredConstructor(long.class, int.class); |
| } catch (NoSuchMethodException | SecurityException e) { |
| // throw new IllegalStateException("Could not get constructor DirectByteBuffer(long, int)", |
| // e); |
| dbbClass = null; |
| dbbCreateFailed = true; |
| return null; |
| } |
| ctor.setAccessible(true); |
| dbbCtor = ctor; |
| } |
| try { |
| return (ByteBuffer) ctor.newInstance(address, size); |
| } catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
| | InvocationTargetException e) { |
| // throw new IllegalStateException("Could not create an instance using DirectByteBuffer(long, |
| // int)", e); |
| dbbClass = null; |
| dbbCtor = null; |
| dbbCreateFailed = true; |
| return null; |
| } |
| } |
| |
| } |