/* * 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.internal.SharedLibrary; import org.apache.geode.pdx.internal.unsafe.UnsafeWrapper; /** * 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 { private static final UnsafeWrapper unsafe; private static final int ARRAY_BYTE_BASE_OFFSET; private static final String reason; static { UnsafeWrapper tmp = null; String tmpReason = null; try { tmp = new UnsafeWrapper(); } 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 (!SharedLibrary.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") private static volatile Class dbbClass = null; @SuppressWarnings("rawtypes") private static volatile Constructor dbbCtor = null; private static volatile boolean dbbCreateFailed = false; private static volatile Method dbbAddressMethod = null; 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; } } }