All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2002-2020 "Neo4j,"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.unsafe.impl.internal.dragons;

import sun.misc.Unsafe;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import org.neo4j.memory.GlobalMemoryTracker;
import org.neo4j.memory.MemoryAllocationTracker;

import static java.lang.Long.compareUnsigned;
import static java.lang.String.format;
import static org.neo4j.util.FeatureToggles.flag;

/**
 * Always check that the Unsafe utilities are available with the {@link UnsafeUtil#assertHasUnsafe} method, before
 * calling any of the other methods.
 * 

* Avoid `import static` for these individual methods. Always qualify method usages with `UnsafeUtil` so use sites * show up in code greps. */ public final class UnsafeUtil { /** * Whether or not to explicitly dirty the allocated memory. This is off by default. * The {@link UnsafeUtil#allocateMemory(long, MemoryAllocationTracker)} method is not guaranteed to allocate * zeroed out memory, but might often do so by pure chance. *

* Enabling this feature will make sure that the allocated memory is full of random data, such that we can test * and verify that our code does not assume that memory is clean when allocated. */ private static final boolean DIRTY_MEMORY = flag( UnsafeUtil.class, "DIRTY_MEMORY", false ); private static final boolean CHECK_NATIVE_ACCESS = flag( UnsafeUtil.class, "CHECK_NATIVE_ACCESS", false ); // this allows us to temporarily disable the checking, for performance: private static boolean nativeAccessCheckEnabled = true; private static final Unsafe unsafe; private static final MethodHandle sharedStringConstructor; private static final String allowUnalignedMemoryAccessProperty = "org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.allowUnalignedMemoryAccess"; private static final ConcurrentSkipListMap allocations = new ConcurrentSkipListMap<>(); private static final ThreadLocal lastUsedAllocation = new ThreadLocal<>(); private static final FreeTrace[] freeTraces = CHECK_NATIVE_ACCESS ? new FreeTrace[4096] : null; private static final AtomicLong freeCounter = new AtomicLong(); public static final Class directByteBufferClass; private static final Constructor directByteBufferCtor; private static final long directByteBufferMarkOffset; private static final long directByteBufferPositionOffset; private static final long directByteBufferLimitOffset; private static final long directByteBufferCapacityOffset; private static final long directByteBufferAddressOffset; private static final int pageSize; public static final boolean allowUnalignedMemoryAccess; public static final boolean storeByteOrderIsNative; static { unsafe = getUnsafe(); MethodHandles.Lookup lookup = MethodHandles.lookup(); sharedStringConstructor = getSharedStringConstructorMethodHandle( lookup ); Class dbbClass = null; Constructor ctor = null; long dbbMarkOffset = 0; long dbbPositionOffset = 0; long dbbLimitOffset = 0; long dbbCapacityOffset = 0; long dbbAddressOffset = 0; int ps = 4096; try { dbbClass = Class.forName( "java.nio.DirectByteBuffer" ); Class bufferClass = Class.forName( "java.nio.Buffer" ); dbbMarkOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "mark" ) ); dbbPositionOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "position" ) ); dbbLimitOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "limit" ) ); dbbCapacityOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "capacity" ) ); dbbAddressOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "address" ) ); ps = unsafe.pageSize(); } catch ( Throwable e ) { if ( dbbClass == null ) { throw new LinkageError( "Cannot to link java.nio.DirectByteBuffer", e ); } try { ctor = dbbClass.getConstructor( Long.TYPE, Integer.TYPE ); ctor.setAccessible( true ); } catch ( NoSuchMethodException e1 ) { throw new LinkageError( "Cannot find JNI constructor for java.nio.DirectByteBuffer", e1 ); } } directByteBufferClass = dbbClass; directByteBufferCtor = ctor; directByteBufferMarkOffset = dbbMarkOffset; directByteBufferPositionOffset = dbbPositionOffset; directByteBufferLimitOffset = dbbLimitOffset; directByteBufferCapacityOffset = dbbCapacityOffset; directByteBufferAddressOffset = dbbAddressOffset; pageSize = ps; // See java.nio.Bits.unaligned() and its uses. String alignmentProperty = System.getProperty( allowUnalignedMemoryAccessProperty ); if ( alignmentProperty != null && (alignmentProperty.equalsIgnoreCase( "true" ) || alignmentProperty.equalsIgnoreCase( "false" )) ) { allowUnalignedMemoryAccess = Boolean.parseBoolean( alignmentProperty ); } else { boolean unaligned; String arch = System.getProperty( "os.arch", "?" ); switch ( arch ) // list of architectures that support unaligned access to memory { case "x86_64": case "i386": case "x86": case "amd64": case "ppc64": case "ppc64le": case "ppc64be": unaligned = true; break; default: unaligned = false; break; } allowUnalignedMemoryAccess = unaligned; } storeByteOrderIsNative = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; } private UnsafeUtil() { } private static Unsafe getUnsafe() { try { PrivilegedExceptionAction getUnsafe = () -> { try { return Unsafe.getUnsafe(); } catch ( Exception e ) { Class type = Unsafe.class; Field[] fields = type.getDeclaredFields(); for ( Field field : fields ) { if ( Modifier.isStatic( field.getModifiers() ) && type.isAssignableFrom( field.getType() ) ) { field.setAccessible( true ); return type.cast( field.get( null ) ); } } LinkageError error = new LinkageError( "No static field of type sun.misc.Unsafe" ); error.addSuppressed( e ); throw error; } }; return AccessController.doPrivileged( getUnsafe ); } catch ( Exception e ) { throw new LinkageError( "Cannot access sun.misc.Unsafe", e ); } } /** * @throws java.lang.LinkageError if the Unsafe tools are not available on in this JVM. */ public static void assertHasUnsafe() { if ( unsafe == null ) { throw new LinkageError( "Unsafe not available" ); } } private static MethodHandle getSharedStringConstructorMethodHandle( MethodHandles.Lookup lookup ) { try { Constructor constructor = String.class.getDeclaredConstructor( char[].class, Boolean.TYPE ); constructor.setAccessible( true ); return lookup.unreflectConstructor( constructor ); } catch ( Exception e ) { return null; } } /** * Get the object-relative field offset. */ public static long getFieldOffset( Class type, String field ) { try { return unsafe.objectFieldOffset( type.getDeclaredField( field ) ); } catch ( NoSuchFieldException e ) { String message = "Could not get offset of '" + field + "' field on type " + type; throw new LinkageError( message, e ); } } /** * Atomically add the given delta to the int field, and return its previous value. *

* This has the memory visibility semantics of a volatile read followed by a volatile write. */ public static int getAndAddInt( Object obj, long offset, int delta ) { return unsafe.getAndAddInt( obj, offset, delta ); } /** * Atomically add the given delta to the long field, and return its previous value. *

* This has the memory visibility semantics of a volatile read followed by a volatile write. */ public static long getAndAddLong( Object obj, long offset, long delta ) { return unsafe.getAndAddLong( obj, offset, delta ); } /** * Orders loads before the fence, with loads and stores after the fence. */ public static void loadFence() { unsafe.loadFence(); } /** * Orders stores before the fence, with loads and stores after the fence. */ public static void storeFence() { unsafe.storeFence(); } /** * Orders loads and stores before the fence, with loads and stores after the fence. */ public static void fullFence() { unsafe.fullFence(); } /** * Atomically compare the current value of the given long field with the expected value, and if they are the * equal, set the field to the updated value and return true. Otherwise return false. *

* If this method returns true, then it has the memory visibility semantics of a volatile read followed by a * volatile write. */ public static boolean compareAndSwapLong( Object obj, long offset, long expected, long update ) { return unsafe.compareAndSwapLong( obj, offset, expected, update ); } /** * Same as compareAndSwapLong, but for object references. */ public static boolean compareAndSwapObject( Object obj, long offset, Object expected, Object update ) { return unsafe.compareAndSwapObject( obj, offset, expected, update ); } /** * Atomically return the current object reference value, and exchange it with the given new reference value. */ public static Object getAndSetObject( Object obj, long offset, Object newValue ) { return unsafe.getAndSetObject( obj, offset, newValue ); } /** * Atomically exchanges provided newValue with the current value of field or array element, with * provided offset. */ public static long getAndSetLong( Object object, long offset, long newValue ) { return unsafe.getAndSetLong( object, offset, newValue ); } /** * Atomically set field or array element to a maximum between current value and provided newValue */ public static void compareAndSetMaxLong( Object object, long fieldOffset, long newValue ) { long currentValue; do { currentValue = UnsafeUtil.getLongVolatile( object, fieldOffset ); if ( currentValue >= newValue ) { return; } } while ( !UnsafeUtil.compareAndSwapLong( object, fieldOffset, currentValue, newValue ) ); } /** * Create a string with a char[] that you know is not going to be modified, so avoid the copy constructor. * * @param chars array that will back the new string * @return the created string */ public static String newSharedArrayString( char[] chars ) { if ( sharedStringConstructor != null ) { try { return (String) sharedStringConstructor.invokeExact( chars, true ); } catch ( Throwable throwable ) { throw new LinkageError( "Unexpected 'String constructor' intrinsic failure", throwable ); } } else { return new String( chars ); } } /** * Allocate a block of memory of the given size in bytes, and return a pointer to that memory. *

* The memory is aligned such that it can be used for any data type. * The memory is uninitialised, so it may contain random garbage, or it may not. * * @return a pointer to the allocated memory */ public static long allocateMemory( long bytes ) throws NativeMemoryAllocationRefusedError { final long pointer; try { pointer = unsafe.allocateMemory( bytes ); } catch ( Throwable e ) { throw new NativeMemoryAllocationRefusedError( bytes, GlobalMemoryTracker.INSTANCE.usedDirectMemory(), e ); } addAllocatedPointer( pointer, bytes ); GlobalMemoryTracker.INSTANCE.allocated( bytes ); if ( DIRTY_MEMORY ) { setMemory( pointer, bytes, (byte) 0xA5 ); } return pointer; } /** * Allocate a block of memory of the given size in bytes and update memory allocation tracker accordingly. *

* The memory is aligned such that it can be used for any data type. * The memory is uninitialised, so it may contain random garbage, or it may not. * @return a pointer to the allocated memory */ public static long allocateMemory( long bytes, MemoryAllocationTracker allocationTracker ) throws NativeMemoryAllocationRefusedError { final long pointer = allocateMemory( bytes ); allocationTracker.allocated( bytes ); return pointer; } /** * Returns address pointer equal to or slightly after the given {@code pointer}. * The returned pointer as aligned with {@code alignBy} such that {@code pointer % alignBy == 0}. * The given pointer should be allocated with at least the requested size + {@code alignBy - 1}, * where the additional bytes will serve as padding for the worst case where the start of the usable * area of the allocated memory will need to be shifted at most {@code alignBy - 1} bytes to the right. *

*


     * 0   4   8   12  16  20        ; 4-byte alignments
     * |---|---|---|---|---|         ; memory
     *        --------===            ; allocated memory (-required, =padding)
     *         ^------^              ; used memory
     * 
* * @param pointer pointer to allocated memory from {@link #allocateMemory(long, MemoryAllocationTracker)} )}. * @param alignBy power-of-two size to align to, e.g. 4 or 8. * @return pointer to place inside the allocated memory to consider the effective start of the * memory, which from that point is aligned by {@code alignBy}. */ public static long alignedMemory( long pointer, int alignBy ) { assert Integer.bitCount( alignBy ) == 1 : "Requires alignment to be power of 2, but was " + alignBy; long misalignment = pointer % alignBy; return misalignment == 0 ? pointer : pointer + (alignBy - misalignment); } /** * Free the memory that was allocated with {@link #allocateMemory} and update memory allocation tracker accordingly. */ public static void free( long pointer, long bytes, MemoryAllocationTracker allocationTracker ) { free( pointer, bytes ); allocationTracker.deallocated( bytes ); } /** * Free the memory that was allocated with {@link #allocateMemory}. */ public static void free( long pointer, long bytes ) { checkFree( pointer ); unsafe.freeMemory( pointer ); GlobalMemoryTracker.INSTANCE.deallocated( bytes ); } private static void addAllocatedPointer( long pointer, long sizeInBytes ) { if ( CHECK_NATIVE_ACCESS ) { allocations.put( pointer, new Allocation( pointer, sizeInBytes, freeCounter.get() ) ); } } private static void checkFree( long pointer ) { if ( CHECK_NATIVE_ACCESS ) { doCheckFree( pointer ); } } private static void doCheckFree( long pointer ) { long count = freeCounter.getAndIncrement(); Allocation allocation = allocations.remove( pointer ); if ( allocation == null ) { StringBuilder sb = new StringBuilder( format( "Bad free: 0x%x, valid pointers are:", pointer ) ); allocations.forEach( ( k, v ) -> sb.append( '\n' ).append( k ) ); throw new AssertionError( sb.toString() ); } int idx = (int) (count & 4095); freeTraces[idx] = new FreeTrace( pointer, allocation, count ); } private static void checkAccess( long pointer, long size ) { if ( CHECK_NATIVE_ACCESS && nativeAccessCheckEnabled ) { doCheckAccess( pointer, size ); } } private static void doCheckAccess( long pointer, long size ) { long boundary = pointer + size; Allocation allocation = lastUsedAllocation.get(); if ( allocation != null ) { if ( compareUnsigned( allocation.pointer, pointer ) <= 0 && compareUnsigned( allocation.boundary, boundary ) > 0 && allocation.freeCounter == freeCounter.get() ) { return; } } Map.Entry fentry = allocations.floorEntry( boundary ); if ( fentry == null || compareUnsigned( fentry.getValue().boundary, boundary ) < 0 ) { Map.Entry centry = allocations.ceilingEntry( pointer ); throwBadAccess( pointer, size, fentry, centry ); } //noinspection ConstantConditions lastUsedAllocation.set( fentry.getValue() ); } private static void throwBadAccess( long pointer, long size, Map.Entry fentry, Map.Entry centry ) { long now = System.nanoTime(); long faddr = fentry == null ? 0 : fentry.getKey(); long fsize = fentry == null ? 0 : fentry.getValue().sizeInBytes; long foffset = pointer - (faddr + fsize); long caddr = centry == null ? 0 : centry.getKey(); long csize = centry == null ? 0 : centry.getValue().sizeInBytes; long coffset = caddr - (pointer + size); boolean floorIsNearest = foffset < coffset; long naddr = floorIsNearest ? faddr : caddr; long nsize = floorIsNearest ? fsize : csize; long noffset = floorIsNearest ? foffset : coffset; List recentFrees = Arrays.stream( freeTraces ) .filter( Objects::nonNull ) .filter( trace -> trace.contains( pointer ) ) .sorted() .collect( Collectors.toList() ); AssertionError error = new AssertionError( format( "Bad access to address 0x%x with size %s, nearest valid allocation is " + "0x%x (%s bytes, off by %s bytes). " + "Recent relevant frees (of %s) are attached as suppressed exceptions.", pointer, size, naddr, nsize, noffset, freeCounter.get() ) ); for ( FreeTrace recentFree : recentFrees ) { recentFree.referenceTime = now; error.addSuppressed( recentFree ); } throw error; } /** * Return the power-of-2 native memory page size. */ public static int pageSize() { return pageSize; } public static void putBoolean( Object obj, long offset, boolean value ) { unsafe.putBoolean( obj, offset, value ); } public static boolean getBoolean( Object obj, long offset ) { return unsafe.getBoolean( obj, offset ); } public static void putBooleanVolatile( Object obj, long offset, boolean value ) { unsafe.putBooleanVolatile( obj, offset, value ); } public static boolean getBooleanVolatile( Object obj, long offset ) { return unsafe.getBooleanVolatile( obj, offset ); } public static void putByte( long address, byte value ) { checkAccess( address, Byte.BYTES ); unsafe.putByte( address, value ); } public static byte getByte( long address ) { checkAccess( address, Byte.BYTES ); return unsafe.getByte( address ); } public static void putByteVolatile( long address, byte value ) { checkAccess( address, Byte.BYTES ); unsafe.putByteVolatile( null, address, value ); } public static byte getByteVolatile( long address ) { checkAccess( address, Byte.BYTES ); return unsafe.getByteVolatile( null, address ); } public static void putByte( Object obj, long offset, byte value ) { unsafe.putByte( obj, offset, value ); } public static byte getByte( Object obj, long offset ) { return unsafe.getByte( obj, offset ); } public static byte getByteVolatile( Object obj, long offset ) { return unsafe.getByteVolatile( obj, offset ); } public static void putByteVolatile( Object obj, long offset, byte value ) { unsafe.putByteVolatile( obj, offset, value ); } public static void putShort( long address, short value ) { checkAccess( address, Short.BYTES ); unsafe.putShort( address, value ); } public static short getShort( long address ) { checkAccess( address, Short.BYTES ); return unsafe.getShort( address ); } public static void putShortVolatile( long address, short value ) { checkAccess( address, Short.BYTES ); unsafe.putShortVolatile( null, address, value ); } public static short getShortVolatile( long address ) { checkAccess( address, Short.BYTES ); return unsafe.getShortVolatile( null, address ); } public static void putShort( Object obj, long offset, short value ) { unsafe.putShort( obj, offset, value ); } public static short getShort( Object obj, long offset ) { return unsafe.getShort( obj, offset ); } public static void putShortVolatile( Object obj, long offset, short value ) { unsafe.putShortVolatile( obj, offset, value ); } public static short getShortVolatile( Object obj, long offset ) { return unsafe.getShortVolatile( obj, offset ); } public static void putFloat( long address, float value ) { checkAccess( address, Float.BYTES ); unsafe.putFloat( address, value ); } public static float getFloat( long address ) { checkAccess( address, Float.BYTES ); return unsafe.getFloat( address ); } public static void putFloatVolatile( long address, float value ) { checkAccess( address, Float.BYTES ); unsafe.putFloatVolatile( null, address, value ); } public static float getFloatVolatile( long address ) { checkAccess( address, Float.BYTES ); return unsafe.getFloatVolatile( null, address ); } public static void putFloat( Object obj, long offset, float value ) { unsafe.putFloat( obj, offset, value ); } public static float getFloat( Object obj, long offset ) { return unsafe.getFloat( obj, offset ); } public static void putFloatVolatile( Object obj, long offset, float value ) { unsafe.putFloatVolatile( obj, offset, value ); } public static float getFloatVolatile( Object obj, long offset ) { return unsafe.getFloatVolatile( obj, offset ); } public static void putChar( long address, char value ) { checkAccess( address, Character.BYTES ); unsafe.putChar( address, value ); } public static char getChar( long address ) { checkAccess( address, Character.BYTES ); return unsafe.getChar( address ); } public static void putCharVolatile( long address, char value ) { checkAccess( address, Character.BYTES ); unsafe.putCharVolatile( null, address, value ); } public static char getCharVolatile( long address ) { checkAccess( address, Character.BYTES ); return unsafe.getCharVolatile( null, address ); } public static void putChar( Object obj, long offset, char value ) { unsafe.putChar( obj, offset, value ); } public static char getChar( Object obj, long offset ) { return unsafe.getChar( obj, offset ); } public static void putCharVolatile( Object obj, long offset, char value ) { unsafe.putCharVolatile( obj, offset, value ); } public static char getCharVolatile( Object obj, long offset ) { return unsafe.getCharVolatile( obj, offset ); } public static void putInt( long address, int value ) { checkAccess( address, Integer.BYTES ); unsafe.putInt( address, value ); } public static int getInt( long address ) { checkAccess( address, Integer.BYTES ); return unsafe.getInt( address ); } public static void putIntVolatile( long address, int value ) { checkAccess( address, Integer.BYTES ); unsafe.putIntVolatile( null, address, value ); } public static int getIntVolatile( long address ) { checkAccess( address, Integer.BYTES ); return unsafe.getIntVolatile( null, address ); } public static void putInt( Object obj, long offset, int value ) { unsafe.putInt( obj, offset, value ); } public static int getInt( Object obj, long offset ) { return unsafe.getInt( obj, offset ); } public static void putIntVolatile( Object obj, long offset, int value ) { unsafe.putIntVolatile( obj, offset, value ); } public static int getIntVolatile( Object obj, long offset ) { return unsafe.getIntVolatile( obj, offset ); } public static void putLongVolatile( long address, long value ) { checkAccess( address, Long.BYTES ); unsafe.putLongVolatile( null, address, value ); } public static long getLongVolatile( long address ) { checkAccess( address, Long.BYTES ); return unsafe.getLongVolatile( null, address ); } public static void putLong( long address, long value ) { checkAccess( address, Long.BYTES ); unsafe.putLong( address, value ); } public static long getLong( long address ) { checkAccess( address, Long.BYTES ); return unsafe.getLong( address ); } public static void putLong( Object obj, long offset, long value ) { unsafe.putLong( obj, offset, value ); } public static long getLong( Object obj, long offset ) { return unsafe.getLong( obj, offset ); } public static void putLongVolatile( Object obj, long offset, long value ) { unsafe.putLongVolatile( obj, offset, value ); } public static long getLongVolatile( Object obj, long offset ) { return unsafe.getLongVolatile( obj, offset ); } public static void putDouble( long address, double value ) { checkAccess( address, Double.BYTES ); unsafe.putDouble( address, value ); } public static double getDouble( long address ) { checkAccess( address, Double.BYTES ); return unsafe.getDouble( address ); } public static void putDoubleVolatile( long address, double value ) { checkAccess( address, Double.BYTES ); unsafe.putDoubleVolatile( null, address, value ); } public static double getDoubleVolatile( long address ) { checkAccess( address, Double.BYTES ); return unsafe.getDoubleVolatile( null, address ); } public static void putDouble( Object obj, long offset, double value ) { unsafe.putDouble( obj, offset, value ); } public static double getDouble( Object obj, long offset ) { return unsafe.getDouble( obj, offset ); } public static void putDoubleVolatile( Object obj, long offset, double value ) { unsafe.putDoubleVolatile( obj, offset, value ); } public static double getDoubleVolatile( Object obj, long offset ) { return unsafe.getDoubleVolatile( obj, offset ); } public static void putObject( Object obj, long offset, Object value ) { unsafe.putObject( obj, offset, value ); } public static Object getObject( Object obj, long offset ) { return unsafe.getObject( obj, offset ); } public static Object getObjectVolatile( Object obj, long offset ) { return unsafe.getObjectVolatile( obj, offset ); } public static void putObjectVolatile( Object obj, long offset, Object value ) { unsafe.putObjectVolatile( obj, offset, value ); } public static int arrayBaseOffset( Class klass ) { return unsafe.arrayBaseOffset( klass ); } public static int arrayIndexScale( Class klass ) { int scale = unsafe.arrayIndexScale( klass ); if ( scale == 0 ) { throw new AssertionError( "Array type too narrow for unsafe access: " + klass ); } return scale; } public static int arrayOffset( int index, int base, int scale ) { return base + index * scale; } /** * Set the given number of bytes to the given value, starting from the given address. */ public static void setMemory( long address, long bytes, byte value ) { checkAccess( address, bytes ); if ( 0 == (address & 1) && bytes > 64 ) { unsafe.putByte( address, value ); unsafe.setMemory( address + 1, bytes - 1, value ); } else { unsafe.setMemory( address, bytes, value ); } } /** * Copy the given number of bytes from the source address to the destination address. */ public static void copyMemory( long srcAddress, long destAddress, long bytes ) { checkAccess( srcAddress, bytes ); unsafe.copyMemory( srcAddress, destAddress, bytes ); } /** * Create a new DirectByteBuffer that wraps the given address and has the given capacity. *

* The ByteBuffer does NOT create a Cleaner, or otherwise register the pointer for freeing. */ public static ByteBuffer newDirectByteBuffer( long addr, int cap ) throws Exception { checkAccess( addr, cap ); if ( directByteBufferCtor == null ) { // Simulate the JNI NewDirectByteBuffer(void*, long) invocation. Object dbb = unsafe.allocateInstance( directByteBufferClass ); initDirectByteBuffer( dbb, addr, cap ); return (ByteBuffer) dbb; } // Reflection based fallback code. return (ByteBuffer) directByteBufferCtor.newInstance( addr, cap ); } /** * Initialize (simulate calling the constructor of) the given DirectByteBuffer. */ public static void initDirectByteBuffer( Object dbb, long addr, int cap ) { checkAccess( addr, cap ); unsafe.putInt( dbb, directByteBufferMarkOffset, -1 ); unsafe.putInt( dbb, directByteBufferPositionOffset, 0 ); unsafe.putInt( dbb, directByteBufferLimitOffset, cap ); unsafe.putInt( dbb, directByteBufferCapacityOffset, cap ); unsafe.putLong( dbb, directByteBufferAddressOffset, addr ); } /** * Read the value of the address field in the (assumed to be) DirectByteBuffer. *

* NOTE: calling this method on a non-direct ByteBuffer is undefined behaviour. * * @param dbb The direct byte buffer to read the address field from. * @return The native memory address in the given direct byte buffer. */ public static long getDirectByteBufferAddress( ByteBuffer dbb ) { return unsafe.getLong( dbb, directByteBufferAddressOffset ); } /** * Change if native access checking is enabled by setting it to the given new setting, and returning the old * setting. *

* This is only useful for speeding up tests when you have a lot of them, and they access native memory a lot. * This does not disable the recording of memory allocations or frees. *

* Remember to restore the old value so other tests in the same JVM get the benefit of native access checks. *

* The changing of this setting is completely unsynchronised, so you have to order this modification before and * after the tests that you want to run without native access checks. * * @param newSetting The new setting. * @return the previous value of this setting. */ public static boolean exchangeNativeAccessCheckEnabled( boolean newSetting ) { boolean previousSetting = nativeAccessCheckEnabled; nativeAccessCheckEnabled = newSetting; return previousSetting; } /** * Gets a {@code short} at memory address {@code p} by reading byte for byte, instead of the whole value * in one go. This can be useful, even necessary in some scenarios where {@link #allowUnalignedMemoryAccess} * is {@code false} and {@code p} isn't aligned properly. Values read with this method should have been * previously put using {@link #putShortByteWiseLittleEndian(long, short)}. * * @param p address pointer to start reading at. * @return the read value, which was read byte for byte. */ public static short getShortByteWiseLittleEndian( long p ) { short a = (short) (UnsafeUtil.getByte( p ) & 0xFF); short b = (short) (UnsafeUtil.getByte( p + 1 ) & 0xFF); return (short) ((b << 8) | a); } /** * Gets a {@code int} at memory address {@code p} by reading byte for byte, instead of the whole value * in one go. This can be useful, even necessary in some scenarios where {@link #allowUnalignedMemoryAccess} * is {@code false} and {@code p} isn't aligned properly. Values read with this method should have been * previously put using {@link #putIntByteWiseLittleEndian(long, int)}. * * @param p address pointer to start reading at. * @return the read value, which was read byte for byte. */ public static int getIntByteWiseLittleEndian( long p ) { int a = UnsafeUtil.getByte( p ) & 0xFF; int b = UnsafeUtil.getByte( p + 1 ) & 0xFF; int c = UnsafeUtil.getByte( p + 2 ) & 0xFF; int d = UnsafeUtil.getByte( p + 3 ) & 0xFF; return (d << 24) | (c << 16) | (b << 8) | a; } /** * Gets a {@code long} at memory address {@code p} by reading byte for byte, instead of the whole value * in one go. This can be useful, even necessary in some scenarios where {@link #allowUnalignedMemoryAccess} * is {@code false} and {@code p} isn't aligned properly. Values read with this method should have been * previously put using {@link #putLongByteWiseLittleEndian(long, long)}. * * @param p address pointer to start reading at. * @return the read value, which was read byte for byte. */ public static long getLongByteWiseLittleEndian( long p ) { long a = UnsafeUtil.getByte( p ) & 0xFF; long b = UnsafeUtil.getByte( p + 1 ) & 0xFF; long c = UnsafeUtil.getByte( p + 2 ) & 0xFF; long d = UnsafeUtil.getByte( p + 3 ) & 0xFF; long e = UnsafeUtil.getByte( p + 4 ) & 0xFF; long f = UnsafeUtil.getByte( p + 5 ) & 0xFF; long g = UnsafeUtil.getByte( p + 6 ) & 0xFF; long h = UnsafeUtil.getByte( p + 7 ) & 0xFF; return (h << 56) | (g << 48) | (f << 40) | (e << 32) | (d << 24) | (c << 16) | (b << 8) | a; } /** * Puts a {@code short} at memory address {@code p} by writing byte for byte, instead of the whole value * in one go. This can be useful, even necessary in some scenarios where {@link #allowUnalignedMemoryAccess} * is {@code false} and {@code p} isn't aligned properly. Values written with this method should be * read using {@link #getShortByteWiseLittleEndian(long)}. * * @param p address pointer to start writing at. * @param value value to write byte for byte. */ public static void putShortByteWiseLittleEndian( long p, short value ) { UnsafeUtil.putByte( p, (byte) value ); UnsafeUtil.putByte( p + 1, (byte) (value >> 8) ); } /** * Puts a {@code int} at memory address {@code p} by writing byte for byte, instead of the whole value * in one go. This can be useful, even necessary in some scenarios where {@link #allowUnalignedMemoryAccess} * is {@code false} and {@code p} isn't aligned properly. Values written with this method should be * read using {@link #getIntByteWiseLittleEndian(long)}. * * @param p address pointer to start writing at. * @param value value to write byte for byte. */ public static void putIntByteWiseLittleEndian( long p, int value ) { UnsafeUtil.putByte( p, (byte) value ); UnsafeUtil.putByte( p + 1, (byte) (value >> 8) ); UnsafeUtil.putByte( p + 2, (byte) (value >> 16) ); UnsafeUtil.putByte( p + 3, (byte) (value >> 24) ); } /** * Puts a {@code long} at memory address {@code p} by writing byte for byte, instead of the whole value * in one go. This can be useful, even necessary in some scenarios where {@link #allowUnalignedMemoryAccess} * is {@code false} and {@code p} isn't aligned properly. Values written with this method should be * read using {@link #getShortByteWiseLittleEndian(long)}. * * @param p address pointer to start writing at. * @param value value to write byte for byte. */ public static void putLongByteWiseLittleEndian( long p, long value ) { UnsafeUtil.putByte( p, (byte) value ); UnsafeUtil.putByte( p + 1, (byte) (value >> 8) ); UnsafeUtil.putByte( p + 2, (byte) (value >> 16) ); UnsafeUtil.putByte( p + 3, (byte) (value >> 24) ); UnsafeUtil.putByte( p + 4, (byte) (value >> 32) ); UnsafeUtil.putByte( p + 5, (byte) (value >> 40) ); UnsafeUtil.putByte( p + 6, (byte) (value >> 48) ); UnsafeUtil.putByte( p + 7, (byte) (value >> 56) ); } private static final class Allocation { private final long pointer; private final long sizeInBytes; private final long boundary; private final long freeCounter; Allocation( long pointer, long sizeInBytes, long freeCounter ) { this.pointer = pointer; this.sizeInBytes = sizeInBytes; this.freeCounter = freeCounter; this.boundary = pointer + sizeInBytes; } @Override public String toString() { return format( "Allocation[pointer=%s (%x), size=%s, boundary=%s (%x), free counter=%s]", pointer, pointer, sizeInBytes, boundary, boundary, freeCounter ); } } private static final class FreeTrace extends Throwable implements Comparable { private final long pointer; private final Allocation allocation; private final long id; private final long nanoTime; private long referenceTime; private FreeTrace( long pointer, Allocation allocation, long id ) { this.pointer = pointer; this.allocation = allocation; this.id = id; this.nanoTime = System.nanoTime(); } private boolean contains( long pointer ) { return this.pointer <= pointer && pointer <= this.pointer + allocation.sizeInBytes; } @Override public int compareTo( FreeTrace that ) { return Long.compare( this.id, that.id ); } @Override public String getMessage() { return format( "0x%x of %6d bytes, freed %s µs ago at", pointer, allocation.sizeInBytes, (referenceTime - nanoTime) / 1000 ); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy