package utils.unsafe; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import com.sun.management.*; import sun.misc.Unsafe; public class OffHeapMemory { private static Unsafe unsafe; private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; private static HotSpotDiagnosticMXBean hotspotMBean; private static Architecture arch; private static int classDefPointerOffset; private static int objectClassDefPointerOffset; private static int sizeFieldOffset; private static int oopSize; public enum Architecture { X86, X64, X64WITHCOMPRESSEDOOPS, X64WITH32BITCOMPRESSEDOOPS } static { Field f; try { f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe unsafe2 = (Unsafe) f.get(null); unsafe = unsafe2; } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } long off1 = 0; long off2 = 0; try { off1 = unsafe.objectFieldOffset(Pointer.class.getField("object")); off2 = unsafe.objectFieldOffset(Pointer.class.getField("address")); } catch (NoSuchFieldException | SecurityException e) { e.printStackTrace(); } oopSize = (int) Math.abs(off2 - off1); String bits = System.getProperty("sun.arch.data.model"); if(bits.equals("32")) { objectClassDefPointerOffset = 4; sizeFieldOffset = 12; classDefPointerOffset = 80; arch = Architecture.X86; } else if(bits.equals("64") && getUseCompressedOopsVMOption()) { objectClassDefPointerOffset = 8; sizeFieldOffset = 24; //arch = oopSize == 4 ? Architecture.X64WITH32BITCOMPRESSEDOOPS : Architecture.X64WITHCOMPRESSEDOOPS; arch = Runtime.getRuntime().maxMemory() <= 2058354688 ? Architecture.X64WITH32BITCOMPRESSEDOOPS : Architecture.X64WITHCOMPRESSEDOOPS; classDefPointerOffset = 84; } else { objectClassDefPointerOffset = 8; sizeFieldOffset = 24; classDefPointerOffset = 160; arch = Architecture.X64; } } private static <T> long getCompressedAddressByShifting(T obj, int compressOopShift) { T[] array = (T[]) new Object[] { obj }; int baseOffset = unsafe.arrayBaseOffset(Object[].class); return normalize(unsafe.getInt(array, baseOffset))/* << compressOopShift*/; } /** * @param size the amount of memory to be allocated * @return the pointer to the allocated memory */ public static long malloc(long size) { return unsafe.allocateMemory(size); } /** * Frees the memory located at the address * @param address the address of the memory which needs to be freed */ public static void free(long address) { unsafe.freeMemory(address); } /** * Allocates a char off heap * @param c the char to be allocated * @return the pointer to the char */ public static long allocateChar(char c) { long pointer = malloc(2); unsafe.putChar(pointer, c); return pointer; } /** * Allocates a byte off heap * @param b the byte to be allocated * @return the pointer to the byte */ public static long allocateByte(byte b) { long pointer = malloc(1); unsafe.putByte(pointer, b); return pointer; } /** * Allocates a short off heap * @param s the short to be allocated * @return the pointer to the short */ public static long allocateShort(short s) { long pointer = malloc(2); unsafe.putShort(pointer, s); return pointer; } /** * Allocates an int off heap * @param i the int to be allocated * @return the pointer to the int */ public static long allocateInt(int i) { long pointer = malloc(4); unsafe.putInt(pointer, i); return pointer; } /** * Allocates an long off heap * @param l the long to be allocated * @return the pointer to the long */ public static long allocateLong(int l) { long pointer = malloc(8); unsafe.putInt(pointer, l); return pointer; } /** * Allocates an float off heap * @param f the float to be allocated * @return the pointer to the float */ public static long allocateFloat(float f) { long pointer = malloc(4); unsafe.putFloat(pointer, f); return pointer; } /** * Allocates an double off heap * @param d the double to be allocated * @return the pointer to the double */ public static long allocateDouble(double d) { long pointer = malloc(8); unsafe.putDouble(pointer, d); return pointer; } /** * Allocates an object off heap * @param o the object to be allocated * @return the pointer to the object */ public static Pointer allocateObject(Object obj) { long size = sizeOf(obj.getClass()); Pointer pointerObj = null; try { pointerObj = (Pointer) unsafe.allocateInstance(Pointer.class); pointerObj.address = malloc(size); unsafe.copyMemory(obj, 0, null, pointerObj.address, size); long objOffset = getUnsafe().objectFieldOffset(Pointer.class.getDeclaredField("object")); // at around 2 GB or smaller heaps the JVM will use 32 bit pointers completely so no oop shift is required // this will work upto 32 GB, after that the JVM has to use a base offset to compensate, and after 64 GB we have to shift left by 4 instead of 3 // unfortunately there is no easy way to get the oops base offset, so users with heaps > 32 GB will have to add -XX:-UseCompressedOops to start the server if(arch == Architecture.X64WITHCOMPRESSEDOOPS) getUnsafe().putLong(pointerObj, objOffset, pointerObj.address >> 3); // set pointer to off-heap copy of the object else getUnsafe().putLong(pointerObj, objOffset, pointerObj.address); } catch (InstantiationException | NoSuchFieldException | SecurityException e) { e.printStackTrace(); } return pointerObj; } private static HotSpotDiagnosticMXBean getHotSpotMBean() { if (hotspotMBean == null) { try { hotspotMBean = ManagementFactory.newPlatformMXBeanProxy( ManagementFactory.getPlatformMBeanServer(), HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class); } catch (IOException e) { e.printStackTrace(); } } return hotspotMBean; } public static boolean getUseCompressedOopsVMOption() { return Boolean.valueOf(getHotSpotMBean().getVMOption("UseCompressedOops").getValue()); } /** * DO NOT CALL THIS UNLESS YOU ARE SURE THE OBJECT IS OFF HEAP OTHERWISE EVERYTHING EXPLODES * @param object the object to be freed */ public static void free(Object object) { unsafe.freeMemory(toAddress(object)); } private static long normalize(int value) { return value & 0xFFFFFFFFL; } public static long toAddress(Object obj) { Object[] array = new Object[] {obj}; long baseOffset = unsafe.arrayBaseOffset(Object[].class); switch(arch) { case X86: return normalize(unsafe.getInt(array, baseOffset)); case X64: return unsafe.getLong(array, baseOffset); case X64WITHCOMPRESSEDOOPS: return (normalize(unsafe.getInt(array, baseOffset)) << 3); default: return normalize(unsafe.getInt(array, baseOffset)); } } /** * This assumes that the address has already been converted for 32 bit and compressed oops VMs * @param address * @return */ public static Object fromAddress(long address) { Object[] array = new Object[] {null}; long baseOffset = unsafe.arrayBaseOffset(Object[].class); unsafe.putLong(array, baseOffset, address); switch(arch) { case X86: unsafe.putInt(array, baseOffset, (int) address); case X64: unsafe.putLong(array, baseOffset, address); case X64WITHCOMPRESSEDOOPS: unsafe.putInt(array, baseOffset, (int) address); default: unsafe.putInt(array, baseOffset, (int) address); } return array[0]; } public static long sizeOf(Object o) { long classAddress = addressOfClassBase(o.getClass()); return unsafe.getInt(classAddress + sizeFieldOffset); /* HashSet<Field> fields = new HashSet<Field>(); Class c = o.getClass(); while (c != Object.class) { for (Field f : c.getDeclaredFields()) { if ((f.getModifiers() & Modifier.STATIC) == 0) { fields.add(f); } } c = c.getSuperclass(); } // get offset long maxSize = 0; for (Field f : fields) { long offset = unsafe.objectFieldOffset(f); if (offset > maxSize) { maxSize = offset; } } return ((maxSize/8) + 1) * 8; // padding*/ } private static long addressOfClassBase(Class<? extends Object> class1) { Object[] objArray = new Object[1]; switch(arch) { case X64: objArray[0] = class1; return unsafe.getLong(objArray, unsafe.arrayBaseOffset(Object[].class)); case X64WITHCOMPRESSEDOOPS: objArray[0] = class1; return normalize(unsafe.getInt(objArray, unsafe.arrayBaseOffset(Object[].class))) << 3; default: case X86: objArray[0] = class1; return unsafe.getInt(objArray, unsafe.arrayBaseOffset(Object[].class)); } } public static long sizeOf(Class<?> clazz) { long maximumOffset = 0; do { for (Field f : clazz.getDeclaredFields()) { if (!Modifier.isStatic(f.getModifiers())) { maximumOffset = Math.max(maximumOffset, unsafe.objectFieldOffset(f)); } } } while ((clazz = clazz.getSuperclass()) != null); return maximumOffset + 8; } public static Unsafe getUnsafe() { return unsafe; } }