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

org.jadira.reflection.access.unsafe.UnsafeOperations Maven / Gradle / Ivy

There is a newer version: 7.0.0.CR1
Show newest version
/*
 *  Copyright 2013 Christopher Pheby
 *
 *  Licensed 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.jadira.reflection.access.unsafe;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.IdentityHashMap;

import org.jadira.reflection.access.api.ClassAccess;
import org.jadira.reflection.access.api.FieldAccess;
import org.jadira.reflection.core.misc.ClassUtils;

/**
 * A set of utility methods for working with sun.misc.Unsafe. Address shallow and deep copying,
 * field access and field manipulation.
 */
@SuppressWarnings("restriction")
public final class UnsafeOperations {

	private static final int REFERENCE_STACK_LIMIT = 150;

	private static final int SIZE_BYTES_BOOLEAN = 1;
	private static final int SIZE_BYTES_BYTE = 1;
	private static final int SIZE_BYTES_CHAR = 2;
	private static final int SIZE_BYTES_SHORT = 2;
	private static final int SIZE_BYTES_INT = 4;
	private static final int SIZE_BYTES_LONG = 8;
	private static final int SIZE_BYTES_FLOAT = 4;
	private static final int SIZE_BYTES_DOUBLE = 8;

	/**
	 * The size of a page that an object will be placed in (always 8 bytes currently) (NB for
	 * HotSpot can be retrieved using ObjectAlignmentInBytes in HotSpotDiagnosticMXBean, but
	 * as this is always 8 for existing JVMs this is hardcoded).
	 */
	private static final int SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT = 8;

	private static final int MIN_SIZE = 16;

	private static final sun.misc.Unsafe THE_UNSAFE;
	private static final boolean IS_UNSAFE_AVAILABLE;

	private static final UnsafeOperations INSTANCE = new UnsafeOperations();

	static {
		boolean isUnsafeAvailable = true;
		sun.misc.Unsafe theUnsafe = null;
		try {
			Class.forName("android.os.Process");
			isUnsafeAvailable = false;

		} catch (ClassNotFoundException e) {
			// Ignored
		} finally {

			if (isUnsafeAvailable) {
				try {
					Field f = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
					f.setAccessible(true);
					theUnsafe = (sun.misc.Unsafe) f.get(null);

				} catch (ClassNotFoundException e) {
					isUnsafeAvailable = false;
				} catch (IllegalArgumentException e) {
					isUnsafeAvailable = false;
				} catch (IllegalAccessException e) {
					isUnsafeAvailable = false;
				} catch (NoSuchFieldException e) {
					isUnsafeAvailable = false;
				} catch (SecurityException e) {
					isUnsafeAvailable = false;
				}
			}
		}
		IS_UNSAFE_AVAILABLE = isUnsafeAvailable;
		THE_UNSAFE = theUnsafe;
	}

	private UnsafeOperations() {
	}

	/**
	 * Returns the (singleton) UnsafeOperations instance
	 * @return UnsafeOperations
	 */
	public static final UnsafeOperations getUnsafeOperations() {
		if (isUnsafeAvailable()) {
			return INSTANCE;
		} else {
			throw new IllegalStateException("Unsafe is not available");
		}
	}

	/**
	 * Check whether the Unsafe API is accessible
	 * @return True if available
	 */
	public static boolean isUnsafeAvailable() {
		return IS_UNSAFE_AVAILABLE;
	}

	/**
	 * Construct and allocate on the heap an instant of the given class, without calling the class constructor
	 * @param clazz Class to create instant for
	 * @param  Type of the instance to be constructed
	 * @return The new instance
	 * @throws IllegalStateException Indicates a problem occurred
	 */
	public final  T allocateInstance(Class clazz) throws IllegalStateException {

		try {
			@SuppressWarnings("unchecked")
			final T result = (T) THE_UNSAFE.allocateInstance(clazz);
			return result;
		} catch (InstantiationException e) {
			throw new IllegalStateException("Cannot allocate instance: " + e.getMessage(), e);
		}
	}

	/**
	 * Gets an offset for the given field relative to the field base. Any particular field will always have the 
	 * same offset, and no two distinct fields of the same class will ever have the same offset.
	 * @param f The Field to determine the offset for
	 * @return The offset represented as a long
	 */
	public final long getObjectFieldOffset(Field f) {
		return THE_UNSAFE.objectFieldOffset(f);
	}

	/**
	 * Performs a shallow copy of the given object - a new instance is allocated with the same contents. Any object
	 * references inside the copy will be the same as the original object.
	 * @param obj Object to copy
	 * @param  The type being copied
	 * @return A new instance, identical to the original
	 */
	public final  T shallowCopy(T obj) {
		long size = shallowSizeOf(obj);
		long address = THE_UNSAFE.allocateMemory(size);
		long start = toAddress(obj);
		THE_UNSAFE.copyMemory(start, address, size);

		@SuppressWarnings("unchecked")
		final T result = (T) fromAddress(address);
		return result;
	}

	/**
	 * Convert the object reference to a memory address represented as a signed long
	 * @param obj The object
	 * @return A long representing the address of the object
	 */
	public final long toAddress(Object obj) {
		Object[] array = new Object[] { obj };
		long baseOffset = THE_UNSAFE.arrayBaseOffset(Object[].class);
		return normalize(THE_UNSAFE.getInt(array, baseOffset));
	}

	/**
	 * Returns the object located at the given memory address
	 * @param address The address (a signed long) for the object
	 * @return The Object at the given address
	 */
	public final Object fromAddress(long address) {
		Object[] array = new Object[] { null };
		long baseOffset = THE_UNSAFE.arrayBaseOffset(Object[].class);
		THE_UNSAFE.putLong(array, baseOffset, address);
		return array[0];
	}

	/**
	 * Copy the value from the given field from the source into the target.
	 * The field specified must contain a primitive
	 * @param source The object to copy from
	 * @param copy The target object
	 * @param field Field to be copied
	 */
	public final void copyPrimitiveField(Object source, Object copy, Field field) {
		copyPrimitiveAtOffset(source, copy, field.getType(), getObjectFieldOffset(field));
	}

	/**
	 * Copies the primitive of the specified type from the given field offset in the source object 
	 * to the same location in the copy
	 * @param source The object to copy from
	 * @param copy The target object
	 * @param type The type of primitive at the given offset - e.g. java.lang.Boolean.TYPE
	 * @param offset The offset to copy from
	 */
	public final void copyPrimitiveAtOffset(Object source, Object copy, Class type, long offset) {

		if (java.lang.Boolean.TYPE == type) {
			boolean origFieldValue = THE_UNSAFE.getBoolean(source, offset);
			THE_UNSAFE.putBoolean(copy, offset, origFieldValue);
		} else if (java.lang.Byte.TYPE == type) {
			byte origFieldValue = THE_UNSAFE.getByte(source, offset);
			THE_UNSAFE.putByte(copy, offset, origFieldValue);
		} else if (java.lang.Character.TYPE == type) {
			char origFieldValue = THE_UNSAFE.getChar(source, offset);
			THE_UNSAFE.putChar(copy, offset, origFieldValue);
		} else if (java.lang.Short.TYPE == type) {
			short origFieldValue = THE_UNSAFE.getShort(source, offset);
			THE_UNSAFE.putShort(copy, offset, origFieldValue);
		} else if (java.lang.Integer.TYPE == type) {
			int origFieldValue = THE_UNSAFE.getInt(source, offset);
			THE_UNSAFE.putInt(copy, offset, origFieldValue);
		} else if (java.lang.Long.TYPE == type) {
			long origFieldValue = THE_UNSAFE.getLong(source, offset);
			THE_UNSAFE.putLong(copy, offset, origFieldValue);
		} else if (java.lang.Float.TYPE == type) {
			float origFieldValue = THE_UNSAFE.getFloat(source, offset);
			THE_UNSAFE.putFloat(copy, offset, origFieldValue);
		} else if (java.lang.Double.TYPE == type) {
			double origFieldValue = THE_UNSAFE.getDouble(source, offset);
			THE_UNSAFE.putDouble(copy, offset, origFieldValue);
		}
	}

	/**
	 * Restores the primitive at the given field to its default value. Default value is defined as the 
	 * value that the field would hold if it was a new, uninitialised value (e.g. false for a boolean).
	 * @param copy The target object
	 * @param type The type of primitive at the given offset - e.g. java.lang.Boolean.TYPE
	 * @param offset The offset to reset to its default value
	 */
	public final void putPrimitiveDefaultAtOffset(Object copy, Class type, long offset) {

		if (java.lang.Boolean.TYPE == type) {
			THE_UNSAFE.putBoolean(copy, offset, false);
		} else if (java.lang.Byte.TYPE == type) {
			THE_UNSAFE.putByte(copy, offset, (byte) 0);
		} else if (java.lang.Character.TYPE == type) {
			THE_UNSAFE.putChar(copy, offset, '\u0000');
		} else if (java.lang.Short.TYPE == type) {
			THE_UNSAFE.putShort(copy, offset, (short) 0);
		} else if (java.lang.Integer.TYPE == type) {
			THE_UNSAFE.putInt(copy, offset, 0);
		} else if (java.lang.Long.TYPE == type) {
			THE_UNSAFE.putLong(copy, offset, 0L);
		} else if (java.lang.Float.TYPE == type) {
			THE_UNSAFE.putFloat(copy, offset, 0.0f);
		} else if (java.lang.Double.TYPE == type) {
			THE_UNSAFE.putDouble(copy, offset, 0.0d);
		}
	}

	/**
	 * Performs a deep copy of the object. With a deep copy all references from the object are also copied.
	 * The identity of referenced objects is preserved, so, for example, if the object graph contains two 
	 * references to the same object, the cloned object will preserve this structure.
	 * @param obj The object to perform a deep copy for.
	 * @param  The type being copied
	 * @return A deep copy of the original object.
	 */
	public  T deepCopy(final T obj) {
		return deepCopy(obj, new IdentityHashMap(10));
	}

	/**
	 * Performs a deep copy of the object. With a deep copy all references from the object are also copied.
	 * The identity of referenced objects is preserved, so, for example, if the object graph contains two 
	 * references to the same object, the cloned object will preserve this structure.
	 * @param o The object to perform a deep copy for.
	 * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
	 * The key is the original object reference - the value is the copied instance for that original.
	 * @param  The type being copied
	 * @return A deep copy of the original object.
	 */
	public  T deepCopy(final T o, IdentityHashMap referencesToReuse) {

		/**
		 * To avoid unnecessary recursion and potential stackoverflow errors, we use an internal
		 * stack
		 */

		final Deque> stack;
		if (referencesToReuse.size() >= REFERENCE_STACK_LIMIT) {
			stack = new ArrayDeque>();
		} else {
			stack = null;
		}

		Object objectInput;

		WorkItem nextWork = null;

		while (true) {

			Object objectResult;

			if (nextWork == null) {
				objectInput = o;
			} else {
				objectInput = getObject(nextWork.getSource(), nextWork.getFieldAccess().fieldOffset());
			}

			if (objectInput == null) {
				objectResult = null;
			} else {

			    if (String.class.isAssignableFrom(objectInput.getClass())) {
			        objectInput = ((String)objectInput);
			    }
			    
				Class clazz = objectInput.getClass();

				if (clazz.isPrimitive() || clazz.isEnum()) {
					objectResult = objectInput;
				} else if (ClassUtils.isJdkImmutable(clazz) || ClassUtils.isWrapper(clazz)) {
					objectResult = objectInput;
				} else {

					final Object result = referencesToReuse.get(objectInput);
					if (result != null) {
						objectResult = result;
					} else {
						if (clazz.isArray()) {
							objectResult = deepCopyArray(objectInput, referencesToReuse);
						} else {

							UnsafeClassAccess classAccess = UnsafeClassAccess.get(clazz);
							objectResult = allocateInstance(objectInput.getClass());

							referencesToReuse.put(objectInput, objectResult);

							ClassAccess classInHierarchy = classAccess;
							
							while (!classInHierarchy.getType().equals(java.lang.Object.class)) {
							
								for (FieldAccess f : classInHierarchy.getDeclaredFieldAccessors()) {
									
									UnsafeFieldAccess uf = (UnsafeFieldAccess)f;
									if (f.fieldClass().isPrimitive()) {
										copyPrimitiveAtOffset(objectInput, objectResult, f.fieldClass(), uf.fieldOffset());
									} else if (stack == null) {
										deepCopyObjectAtOffset(objectInput, objectResult, f.fieldClass(), uf.fieldOffset(), referencesToReuse);
									} else {
										@SuppressWarnings({ "unchecked", "rawtypes" })
										final WorkItem item = new WorkItem(objectInput, objectResult, uf);
										stack.addFirst(item);
									}
								}
							classInHierarchy = classInHierarchy.getSuperClassAccess();
							}
						}
					}
				}
			}

			if (nextWork == null) {
				nextWork = (stack == null ? null : stack.pollFirst());
				if (nextWork == null) {
					@SuppressWarnings("unchecked")
					final T convertedResult = (T) objectResult;
					return convertedResult;
				}
			} else if (nextWork != null) {
				if (objectResult == null) {
					putNullObject(nextWork.getTarget(), nextWork.getFieldAccess().fieldOffset());
				} else {
					putObject(nextWork.getTarget(), nextWork.getFieldAccess().fieldOffset(), objectResult);
				}
				nextWork = (stack == null ? null : stack.pollFirst());
			}
		}
	}

	/**
	 * Copies the object of the specified type from the given field offset in the source object 
	 * to the same location in the copy, visiting the object during the copy so that its fields are also copied
	 * @param source The object to copy from
	 * @param copy The target object
	 * @param fieldClass The declared type of object at the given offset
	 * @param offset The offset to copy from
	 */	
	public final void deepCopyObjectAtOffset(Object source, Object copy, Class fieldClass, long offset) {
		deepCopyObjectAtOffset(source, copy, fieldClass, offset, new IdentityHashMap(100));
	}

	/**
	 * Copies the object of the specified type from the given field offset in the source object 
	 * to the same location in the copy, visiting the object during the copy so that its fields are also copied
	 * @param source The object to copy from
	 * @param copy The target object
	 * @param fieldClass The declared type of object at the given offset
	 * @param offset The offset to copy from
	 * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
	 * The key is the original object reference - the value is the copied instance for that original.
	 */	
	public final void deepCopyObjectAtOffset(Object source, Object copy, Class fieldClass, long offset, IdentityHashMap referencesToReuse) {

		Object origFieldValue = THE_UNSAFE.getObject(source, offset);

		if (origFieldValue == null) {

			putNullObject(copy, offset);
		} else {

			final Object copyFieldValue = deepCopy(origFieldValue, referencesToReuse);
			UnsafeOperations.THE_UNSAFE.putObject(copy, offset, copyFieldValue);
		}
	}

	/**
	 * Copies the object of the specified type from the given field in the source object 
	 * to the same field in the copy, visiting the object during the copy so that its fields are also copied
	 * @param source The object to copy from
	 * @param copy The target object
	 * @param field Field to be copied
	 * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
	 * The key is the original object reference - the value is the copied instance for that original.
	 */	
	public final void deepCopyObjectField(Object source, Object copy, Field field, IdentityHashMap referencesToReuse) {

		deepCopyObjectAtOffset(source, copy, field.getType(), getObjectFieldOffset(field), referencesToReuse);
	}

	/**
	 * Copies the object of the specified type from the given field in the source object 
	 * to the same field in the copy, visiting the object during the copy so that its fields are also copied
	 * @param obj The object to copy from
	 * @param copy The target object
	 * @param field Field to be copied
	 */	
	public final void deepCopyObjectField(Object obj, Object copy, Field field) {

		deepCopyObjectAtOffset(obj, copy, field.getType(), getObjectFieldOffset(field), new IdentityHashMap(100));
	}

	/**
	 * Copies the array of the specified type from the given field offset in the source object 
	 * to the same location in the copy, visiting the array during the copy so that its contents are also copied
	 * @param source The object to copy from
	 * @param copy The target object
	 * @param fieldClass The declared type of array at the given offset
	 * @param offset The offset to copy from
	 */	
	public final void deepCopyArrayAtOffset(Object source, Object copy, Class fieldClass, long offset) {
		deepCopyArrayAtOffset(source, copy, fieldClass, offset, new IdentityHashMap(100));
	}

	/**
	 * Copies the array of the specified type from the given field offset in the source object 
	 * to the same location in the copy, visiting the array during the copy so that its contents are also copied
	 * @param source The object to copy from
	 * @param copy The target object
	 * @param fieldClass The declared type of array at the given offset
	 * @param offset The offset to copy from
	 * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
	 * The key is the original object reference - the value is the copied instance for that original.
	 */	
	public final void deepCopyArrayAtOffset(Object source, Object copy, Class fieldClass, long offset, IdentityHashMap referencesToReuse) {

		Object origFieldValue = THE_UNSAFE.getObject(source, offset);

		if (origFieldValue == null) {

			putNullObject(copy, offset);
		} else {

			final Object copyFieldValue = deepCopyArray(origFieldValue, referencesToReuse);
			UnsafeOperations.THE_UNSAFE.putObject(copy, offset, copyFieldValue);
		}
	}

	/**
	 * Copies the array of the specified type from the given field in the source object 
	 * to the same field in the copy, visiting the array  during the copy so that its contents are also copied
	 * @param obj The object to copy from
	 * @param copy The target object
	 * @param field Field to be copied
	 * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
	 * The key is the original object reference - the value is the copied instance for that original.
	 */	
	public final void deepCopyArrayField(Object obj, Object copy, Field field, IdentityHashMap referencesToReuse) {

		deepCopyArrayAtOffset(obj, copy, field.getType(), getObjectFieldOffset(field), referencesToReuse);
	}

	/**
	 * Copies the array of the specified type from the given field in the source object 
	 * to the same field in the copy, visiting the array  during the copy so that its contents are also copied
	 * @param obj The object to copy from
	 * @param copy The target object
	 * @param field Field to be copied
	 */	
	public final void deepCopyArrayField(Object obj, Object copy, Field field) {

		deepCopyArrayAtOffset(obj, copy, field.getType(), getObjectFieldOffset(field), new IdentityHashMap(100));
	}

	/**
	 * Performs a deep copy of the array. With a deep copy all references from the array are also copied.
	 * The identity of referenced objects is preserved, so, for example, if the object graph contains two 
	 * references to the same object, the cloned object will preserve this structure.
	 * @param arrayOriginal The array to perform a deep copy for.
	 * @param visited An identity map of references to reuse - this is further populated as the copy progresses.
	 * The key is the original object reference - the value is the copied instance for that original.
	 * @return A deep copy of the original array.
	 */
	public final Object deepCopyArray(Object arrayOriginal, IdentityHashMap visited) {

		if (visited.containsKey(arrayOriginal)) {
			return visited.get(arrayOriginal);
		}

		final Class componentType = arrayOriginal.getClass().getComponentType();

		Object result = null;

		if (componentType.isPrimitive()) {

			if (java.lang.Boolean.TYPE == componentType) {
				result = Arrays.copyOf((boolean[]) arrayOriginal, ((boolean[]) arrayOriginal).length);
			} else if (java.lang.Byte.TYPE == componentType) {
				result = Arrays.copyOf((byte[]) arrayOriginal, ((byte[]) arrayOriginal).length);
			} else if (java.lang.Character.TYPE == componentType) {
				result = Arrays.copyOf((char[]) arrayOriginal, ((char[]) arrayOriginal).length);
			} else if (java.lang.Short.TYPE == componentType) {
				result = Arrays.copyOf((short[]) arrayOriginal, ((short[]) arrayOriginal).length);
			} else if (java.lang.Integer.TYPE == componentType) {
				result = Arrays.copyOf((int[]) arrayOriginal, ((int[]) arrayOriginal).length);
			} else if (java.lang.Long.TYPE == componentType) {
				result = Arrays.copyOf((long[]) arrayOriginal, ((long[]) arrayOriginal).length);
			} else if (java.lang.Float.TYPE == componentType) {
				result = Arrays.copyOf((float[]) arrayOriginal, ((float[]) arrayOriginal).length);
			} else if (java.lang.Double.TYPE == componentType) {
				result = Arrays.copyOf((double[]) arrayOriginal, ((double[]) arrayOriginal).length);
			}
		}

		if (result == null) {
			Object[] arrayCopy = Arrays.copyOf((Object[]) arrayOriginal, ((Object[]) arrayOriginal).length);
			if (arrayCopy.length > 0) {

				if (componentType.isArray()) {
					for (int i = 0; i < arrayCopy.length; i++) {
						arrayCopy[i] = deepCopyArray(arrayCopy[i], visited);
					}
				} else {
					for (int i = 0; i < arrayCopy.length; i++) {
						Object component = deepCopy(arrayCopy[i], visited);
						arrayCopy[i] = component;
					}
				}
			}
			result = arrayCopy;
		}

		visited.put(arrayOriginal, result);
		return result;
	}

	/**
	 * Determines the shallow memory size of an instance of the given class
	 * @param clazz The class to calculate the shallow size for
	 * @return Size in bytes
	 */
	public final long shallowSizeOf(Class clazz) {
		return doShallowSizeOfClass(clazz);
	}

	/**
	 * Determines the shallow memory size of the given object (object or array)
	 * @param obj The object instance to calculate the shallow size for
	 * @return Size in bytes
	 */
	public final long shallowSizeOf(Object obj) {

		if (obj == null) {
			return 0;
		}

		if (obj.getClass().isArray()) {
			return doShallowSizeOfArray(obj);
		} else {
			return doShallowSizeOfClass(obj.getClass());
		}
	}

	private long doShallowSizeOfArray(Object array) {

		long size = getSizeOfArrayHeader();
		final int length = Array.getLength(array);
		if (length > 0) {

			Class type = array.getClass().getComponentType();
			if (type.isPrimitive()) {

				if (java.lang.Boolean.TYPE == type) {
					size = size + (length * SIZE_BYTES_BOOLEAN);
				} else if (java.lang.Byte.TYPE == type) {
					size = size + (length * SIZE_BYTES_BYTE);
				} else if (java.lang.Character.TYPE == type) {
					size = size + (length * SIZE_BYTES_CHAR);
				} else if (java.lang.Short.TYPE == type) {
					size = size + (length * SIZE_BYTES_SHORT);
				} else if (java.lang.Integer.TYPE == type) {
					size = size + (length * SIZE_BYTES_INT);
				} else if (java.lang.Long.TYPE == type) {
					size = size + (length * SIZE_BYTES_LONG);
				} else if (java.lang.Float.TYPE == type) {
					size = size + (length * SIZE_BYTES_FLOAT);
				} else if (java.lang.Double.TYPE == type) {
					size = size + (length * SIZE_BYTES_DOUBLE);
				}

			} else {
				size = size + (length * getSizeOfObjectHeader());
			}
		}

		size = size + SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT - 1L;
		return size - (size % SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT);
	}

	private long doShallowSizeOfClass(Class clazz) {

		if (clazz.isArray()) {
			throw new IllegalArgumentException("Shallow Size of cannot be calculated for arrays classes as component length is needed");
		}
		if (clazz.isPrimitive()) {

			return getSizeForPrimitive(clazz);
		}
		if (clazz == Object.class) {
			return MIN_SIZE;
		}

		long size = getSizeOfObjectHeader();

		Field[] fields = ClassUtils.collectInstanceFields(clazz);

		for (Field f : fields) {

			Class fieldClass = f.getType();
			final int fieldSize = fieldClass.isPrimitive() ? getSizeForPrimitive(fieldClass) : getSizeOfObjectHeader();
			final long offsetPlusSize = getObjectFieldOffset(f) + fieldSize;

			if (offsetPlusSize > size) {
				size = offsetPlusSize;
			}
		}

		size = size + SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT - 1L;
		return size - (size % SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT);
	}

	/**
	 * Determines the deep memory size of the given object (object or array), visiting all its references
	 * @param o The object instance to calculate the deep size for
	 * @return Size in bytes
	 */
	public final long deepSizeOf(Object o) {

		IdentityHashMap seenObjects = new IdentityHashMap(10);
		return doDeepSizeOf(o, seenObjects);
	}

	private long doDeepSizeOf(Object o, IdentityHashMap seenObjects) {

		if (o == null) {
			return 0;
		}

		Class clazz = o.getClass();
		if (clazz.isPrimitive()) {
			return getSizeForPrimitive(clazz);
		}

		seenObjects.put(o, Boolean.TRUE);

		if (clazz.isArray()) {

			long size = doShallowSizeOfArray(o);

			if (!clazz.getComponentType().isPrimitive()) {

				Object[] array = (Object[]) o;
				for (int i = 0; i < array.length; i++) {
					Object nextObject = array[i];
					if (nextObject != null && !seenObjects.containsKey(nextObject)) {
						size = size + doDeepSizeOf(nextObject, seenObjects);
					}
				}
			}

			return size;

		} else {

			if (clazz == Object.class) {
				return MIN_SIZE;
			}

			long size = getSizeOfObjectHeader();
			long additionalSize = 0;

			Field[] fields = ClassUtils.collectInstanceFields(clazz);

			for (Field f : fields) {

				long objectFieldOffset = getObjectFieldOffset(f);

				Class fieldClass = f.getType();
				final int fieldSize = fieldClass.isPrimitive() ? getSizeForPrimitive(fieldClass) : getSizeOfObjectHeader();
				final long offsetPlusSize = objectFieldOffset + fieldSize;

				if (offsetPlusSize > size) {
					size = offsetPlusSize;
				}

				if (!fieldClass.isPrimitive()) {
					Object fieldObject = getObject(o, objectFieldOffset);
					if (fieldObject != null && !seenObjects.containsKey(fieldObject)) {
						additionalSize += doDeepSizeOf(fieldObject, seenObjects);
					}
				}
			}

			size = size + SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT - 1L;
			size = size - (size % SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT);

			return size + additionalSize;
		}
	}

	/**
	 * Memory address payload is an unsigned value - we need to normalise the 'sign' to get a
	 * meaningful value back - when we do this we need to store it into a long
	 * @param value The value to normalise
	 * @return The normalised value as a long
	 */
	private static long normalize(int value) {
		if (value >= 0) {
			return value;
		}
		return (~0L >>> 32) & value;
	}

	/**
	 * Get the size in bytes of a native pointer - either 4 or 8 (for 32-bit or 64-bit JRE).
	 * For primitive types, the size is determined by their data type rather than this value.
	 * @return The address size
	 */
	public final int getAddressSize() {
		return THE_UNSAFE.addressSize();
	}	
	
	/**
	 * Retrieve the object at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @return The retrieved object
	 */
	public final Object getObject(Object parent, long offset) {
		return THE_UNSAFE.getObject(parent, offset);
	}

	/**
	 * Retrieve the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @return The retrieved boolean
	 */
	public final boolean getBoolean(Object parent, long offset) {
		return THE_UNSAFE.getBoolean(parent, offset);
	}

	/**
	 * Retrieve the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @return The retrieved char
	 */
	public final char getChar(Object parent, long offset) {
		return THE_UNSAFE.getChar(parent, offset);
	}
	
	/**
	 * Retrieve the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @return The retrieved short
	 */
	public final short getShort(Object parent, long offset) {
		return THE_UNSAFE.getShort(parent, offset);
	}
	
	/**
	 * Retrieve the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @return The retrieved int
	 */
	public final int getInt(Object parent, long offset) {
		return THE_UNSAFE.getInt(parent, offset);
	}
	
	/**
	 * Retrieve the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @return The retrieved long
	 */
	public final long getLong(Object parent, long offset) {
		return THE_UNSAFE.getLong(parent, offset);
	}
	
	/**
	 * Retrieve the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @return The retrieved float
	 */
	public final float getFloat(Object parent, long offset) {
		return THE_UNSAFE.getFloat(parent, offset);
	}
	
	/**
	 * Retrieve the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @return The retrieved double
	 */
	public final double getDouble(Object parent, long offset) {
		return THE_UNSAFE.getDouble(parent, offset);
	}

	/**
	 * Retrieve the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @return The retrieved byte
	 */
	public final byte getByte(Object parent, long offset) {
		return THE_UNSAFE.getByte(parent, offset);
	}

	/**
	 * Write a null value to the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 */
	public final void putNullObject(Object parent, long offset) {
		THE_UNSAFE.putObject(parent, offset, null);
	}

	/**
	 * Resets to false the boolean value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 */
	public final void putDefaultBoolean(Object parent, long offset) {
		THE_UNSAFE.putBoolean(parent, offset, false);
	}

	/**
	 * Resets to u0000 the char value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 */
	public final void putDefaultChar(Object parent, long offset) {
		THE_UNSAFE.putChar(parent, offset, '\u0000');
	}
	
	/**
	 * Resets to 0 the short value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 */
	public final void putDefaultShort(Object parent, long offset) {
		THE_UNSAFE.putShort(parent, offset, (short)0);
	}

	/**
	 * Resets to 0 the int value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 */
	public final void putDefaultInt(Object parent, long offset) {
		THE_UNSAFE.putInt(parent, offset, 0);
	}
	
	/**
	 * Resets to 0 the long value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 */
	public final void putDefaultLong(Object parent, long offset) {
		THE_UNSAFE.putLong(parent, offset, 0L);
	}
	
	/**
	 * Resets to 0.0f the float value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 */
	public final void putDefaultFloat(Object parent, long offset) {
		THE_UNSAFE.putFloat(parent, offset, 0.0F);
	}
	
	/**
	 * Resets to 0.0d the double value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 */
	public final void putDefaultDouble(Object parent, long offset) {
		THE_UNSAFE.putDouble(parent, offset, 0.0D);
	}
	
	/**
	 * Resets to 0 the byte value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 */
	public final void putDefaultByte(Object parent, long offset) {
		THE_UNSAFE.putByte(parent, offset, (byte)0);
	}

	/**
	 * Puts the object's reference at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @param value Object to be put
	 */
	public final void putObject(Object parent, long offset, Object value) {
		THE_UNSAFE.putObject(parent, offset, value);
	}

	/**
	 * Puts the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @param value boolean to be put
	 */
	public final void putBoolean(Object parent, long offset, boolean value) {
		THE_UNSAFE.putBoolean(parent, offset, value);
	}

	/**
	 * Puts the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @param value char to be put
	 */
	public final void putChar(Object parent, long offset, char value) {
		THE_UNSAFE.putChar(parent, offset, value);
	}
	
	/**
	 * Puts the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @param value short to be put
	 */
	public final void putShort(Object parent, long offset, short value) {
		THE_UNSAFE.putShort(parent, offset, value);
	}
	
	/**
	 * Puts the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @param value int to be put
	 */
	public final void putInt(Object parent, long offset, int value) {
		THE_UNSAFE.putInt(parent, offset, value);
	}
	
	/**
	 * Puts the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @param value long to be put
	 */
	public final void putLong(Object parent, long offset, long value) {
		THE_UNSAFE.putLong(parent, offset, value);
	}
	
	/**
	 * Puts the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @param value float to be put
	 */
	public final void putFloat(Object parent, long offset, float value) {
		THE_UNSAFE.putFloat(parent, offset, value);
	}
	
	/**
	 * Puts the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @param value double to be put
	 */
	public final void putDouble(Object parent, long offset, double value) {
		THE_UNSAFE.putDouble(parent, offset, value);
	}
	
	/**
	 * Puts the value at the given offset of the supplied parent object
	 * @param parent The Object's parent
	 * @param offset The offset
	 * @param value byte to be put
	 */
	public final void putByte(Object parent, long offset, byte value) {
		THE_UNSAFE.putByte(parent, offset, value);
	}
	
	private class WorkItem

{ private final Object source; private final Object target; private final UnsafeFieldAccess

fieldAccess; public WorkItem(Object source, Object target, UnsafeFieldAccess

fieldAccess) { this.source = source; this.target = target; this.fieldAccess = fieldAccess; } public Object getSource() { return source; } public Object getTarget() { return target; } public UnsafeFieldAccess

getFieldAccess() { return fieldAccess; } } @SuppressWarnings("unused") private static final class SingleFieldHolder { public int field; } private final int getSizeOfArrayHeader() { return THE_UNSAFE.arrayBaseOffset(byte[].class); } private final int getSizeForPrimitive(Class clazz) { if (java.lang.Boolean.TYPE.isAssignableFrom(clazz)) { return SIZE_BYTES_BOOLEAN; } else if (java.lang.Byte.TYPE.isAssignableFrom(clazz)) { return SIZE_BYTES_BYTE; } else if (java.lang.Character.TYPE.isAssignableFrom(clazz)) { return SIZE_BYTES_CHAR; } else if (java.lang.Short.TYPE.isAssignableFrom(clazz)) { return SIZE_BYTES_SHORT; } else if (java.lang.Integer.TYPE.isAssignableFrom(clazz)) { return SIZE_BYTES_INT; } else if (java.lang.Long.TYPE.isAssignableFrom(clazz)) { return SIZE_BYTES_LONG; } else if (java.lang.Float.TYPE.isAssignableFrom(clazz)) { return SIZE_BYTES_FLOAT; } else if (java.lang.Double.TYPE.isAssignableFrom(clazz)) { return SIZE_BYTES_DOUBLE; } throw new IllegalArgumentException("Class " + clazz.getName() + " is not primitive"); } private final int getSizeOfObjectHeader() { try { return (int) THE_UNSAFE.objectFieldOffset(SingleFieldHolder.class.getDeclaredField("field")); } catch (NoSuchFieldException e) { throw new IllegalStateException("Cannot determine size of object header", e); } catch (SecurityException e) { throw new IllegalStateException("Cannot determine size of object header", e); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy