001/*
002 *  Copyright 2013 Christopher Pheby
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.jadira.reflection.access.unsafe;
017
018import java.lang.reflect.Array;
019import java.lang.reflect.Field;
020import java.util.ArrayDeque;
021import java.util.Arrays;
022import java.util.Deque;
023import java.util.IdentityHashMap;
024
025import org.jadira.reflection.access.api.ClassAccess;
026import org.jadira.reflection.access.api.FieldAccess;
027import org.jadira.reflection.core.misc.ClassUtils;
028
029/**
030 * A set of utility methods for working with sun.misc.Unsafe. Address shallow and deep copying,
031 * field access and field manipulation.
032 */
033@SuppressWarnings("restriction")
034public final class UnsafeOperations {
035
036        private static final int REFERENCE_STACK_LIMIT = 150;
037
038        private static final int SIZE_BYTES_BOOLEAN = 1;
039        private static final int SIZE_BYTES_BYTE = 1;
040        private static final int SIZE_BYTES_CHAR = 2;
041        private static final int SIZE_BYTES_SHORT = 2;
042        private static final int SIZE_BYTES_INT = 4;
043        private static final int SIZE_BYTES_LONG = 8;
044        private static final int SIZE_BYTES_FLOAT = 4;
045        private static final int SIZE_BYTES_DOUBLE = 8;
046
047        /**
048         * The size of a page that an object will be placed in (always 8 bytes currently) (NB for
049         * HotSpot can be retrieved using ObjectAlignmentInBytes in HotSpotDiagnosticMXBean, but
050         * as this is always 8 for existing JVMs this is hardcoded).
051         */
052        private static final int SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT = 8;
053
054        private static final int MIN_SIZE = 16;
055
056        private static final sun.misc.Unsafe THE_UNSAFE;
057        private static final boolean IS_UNSAFE_AVAILABLE;
058
059        private static final UnsafeOperations INSTANCE = new UnsafeOperations();
060
061        static {
062                boolean isUnsafeAvailable = true;
063                sun.misc.Unsafe theUnsafe = null;
064                try {
065                        Class.forName("android.os.Process");
066                        isUnsafeAvailable = false;
067
068                } catch (ClassNotFoundException e) {
069                        // Ignored
070                } finally {
071
072                        if (isUnsafeAvailable) {
073                                try {
074                                        Field f = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
075                                        f.setAccessible(true);
076                                        theUnsafe = (sun.misc.Unsafe) f.get(null);
077
078                                } catch (ClassNotFoundException e) {
079                                        isUnsafeAvailable = false;
080                                } catch (IllegalArgumentException e) {
081                                        isUnsafeAvailable = false;
082                                } catch (IllegalAccessException e) {
083                                        isUnsafeAvailable = false;
084                                } catch (NoSuchFieldException e) {
085                                        isUnsafeAvailable = false;
086                                } catch (SecurityException e) {
087                                        isUnsafeAvailable = false;
088                                }
089                        }
090                }
091                IS_UNSAFE_AVAILABLE = isUnsafeAvailable;
092                THE_UNSAFE = theUnsafe;
093        }
094
095        private UnsafeOperations() {
096        }
097
098        /**
099         * Returns the (singleton) UnsafeOperations instance
100         * @return UnsafeOperations
101         */
102        public static final UnsafeOperations getUnsafeOperations() {
103                if (isUnsafeAvailable()) {
104                        return INSTANCE;
105                } else {
106                        throw new IllegalStateException("Unsafe is not available");
107                }
108        }
109
110        /**
111         * Check whether the Unsafe API is accessible
112         * @return True if available
113         */
114        public static boolean isUnsafeAvailable() {
115                return IS_UNSAFE_AVAILABLE;
116        }
117
118        /**
119         * Construct and allocate on the heap an instant of the given class, without calling the class constructor
120         * @param clazz Class to create instant for
121         * @param <T> Type of the instance to be constructed
122         * @return The new instance
123         * @throws IllegalStateException Indicates a problem occurred
124         */
125        public final <T> T allocateInstance(Class<T> clazz) throws IllegalStateException {
126
127                try {
128                        @SuppressWarnings("unchecked")
129                        final T result = (T) THE_UNSAFE.allocateInstance(clazz);
130                        return result;
131                } catch (InstantiationException e) {
132                        throw new IllegalStateException("Cannot allocate instance: " + e.getMessage(), e);
133                }
134        }
135
136        /**
137         * Gets an offset for the given field relative to the field base. Any particular field will always have the 
138         * same offset, and no two distinct fields of the same class will ever have the same offset.
139         * @param f The Field to determine the offset for
140         * @return The offset represented as a long
141         */
142        public final long getObjectFieldOffset(Field f) {
143                return THE_UNSAFE.objectFieldOffset(f);
144        }
145
146        /**
147         * Performs a shallow copy of the given object - a new instance is allocated with the same contents. Any object
148         * references inside the copy will be the same as the original object.
149         * @param obj Object to copy
150         * @param <T> The type being copied
151         * @return A new instance, identical to the original
152         */
153        public final <T> T shallowCopy(T obj) {
154                long size = shallowSizeOf(obj);
155                long address = THE_UNSAFE.allocateMemory(size);
156                long start = toAddress(obj);
157                THE_UNSAFE.copyMemory(start, address, size);
158
159                @SuppressWarnings("unchecked")
160                final T result = (T) fromAddress(address);
161                return result;
162        }
163
164        /**
165         * Convert the object reference to a memory address represented as a signed long
166         * @param obj The object
167         * @return A long representing the address of the object
168         */
169        public final long toAddress(Object obj) {
170                Object[] array = new Object[] { obj };
171                long baseOffset = THE_UNSAFE.arrayBaseOffset(Object[].class);
172                return normalize(THE_UNSAFE.getInt(array, baseOffset));
173        }
174
175        /**
176         * Returns the object located at the given memory address
177         * @param address The address (a signed long) for the object
178         * @return The Object at the given address
179         */
180        public final Object fromAddress(long address) {
181                Object[] array = new Object[] { null };
182                long baseOffset = THE_UNSAFE.arrayBaseOffset(Object[].class);
183                THE_UNSAFE.putLong(array, baseOffset, address);
184                return array[0];
185        }
186
187        /**
188         * Copy the value from the given field from the source into the target.
189         * The field specified must contain a primitive
190         * @param source The object to copy from
191         * @param copy The target object
192         * @param field Field to be copied
193         */
194        public final void copyPrimitiveField(Object source, Object copy, Field field) {
195                copyPrimitiveAtOffset(source, copy, field.getType(), getObjectFieldOffset(field));
196        }
197
198        /**
199         * Copies the primitive of the specified type from the given field offset in the source object 
200         * to the same location in the copy
201         * @param source The object to copy from
202         * @param copy The target object
203         * @param type The type of primitive at the given offset - e.g. java.lang.Boolean.TYPE
204         * @param offset The offset to copy from
205         */
206        public final void copyPrimitiveAtOffset(Object source, Object copy, Class<?> type, long offset) {
207
208                if (java.lang.Boolean.TYPE == type) {
209                        boolean origFieldValue = THE_UNSAFE.getBoolean(source, offset);
210                        THE_UNSAFE.putBoolean(copy, offset, origFieldValue);
211                } else if (java.lang.Byte.TYPE == type) {
212                        byte origFieldValue = THE_UNSAFE.getByte(source, offset);
213                        THE_UNSAFE.putByte(copy, offset, origFieldValue);
214                } else if (java.lang.Character.TYPE == type) {
215                        char origFieldValue = THE_UNSAFE.getChar(source, offset);
216                        THE_UNSAFE.putChar(copy, offset, origFieldValue);
217                } else if (java.lang.Short.TYPE == type) {
218                        short origFieldValue = THE_UNSAFE.getShort(source, offset);
219                        THE_UNSAFE.putShort(copy, offset, origFieldValue);
220                } else if (java.lang.Integer.TYPE == type) {
221                        int origFieldValue = THE_UNSAFE.getInt(source, offset);
222                        THE_UNSAFE.putInt(copy, offset, origFieldValue);
223                } else if (java.lang.Long.TYPE == type) {
224                        long origFieldValue = THE_UNSAFE.getLong(source, offset);
225                        THE_UNSAFE.putLong(copy, offset, origFieldValue);
226                } else if (java.lang.Float.TYPE == type) {
227                        float origFieldValue = THE_UNSAFE.getFloat(source, offset);
228                        THE_UNSAFE.putFloat(copy, offset, origFieldValue);
229                } else if (java.lang.Double.TYPE == type) {
230                        double origFieldValue = THE_UNSAFE.getDouble(source, offset);
231                        THE_UNSAFE.putDouble(copy, offset, origFieldValue);
232                }
233        }
234
235        /**
236         * Restores the primitive at the given field to its default value. Default value is defined as the 
237         * value that the field would hold if it was a new, uninitialised value (e.g. false for a boolean).
238         * @param copy The target object
239         * @param type The type of primitive at the given offset - e.g. java.lang.Boolean.TYPE
240         * @param offset The offset to reset to its default value
241         */
242        public final void putPrimitiveDefaultAtOffset(Object copy, Class<?> type, long offset) {
243
244                if (java.lang.Boolean.TYPE == type) {
245                        THE_UNSAFE.putBoolean(copy, offset, false);
246                } else if (java.lang.Byte.TYPE == type) {
247                        THE_UNSAFE.putByte(copy, offset, (byte) 0);
248                } else if (java.lang.Character.TYPE == type) {
249                        THE_UNSAFE.putChar(copy, offset, '\u0000');
250                } else if (java.lang.Short.TYPE == type) {
251                        THE_UNSAFE.putShort(copy, offset, (short) 0);
252                } else if (java.lang.Integer.TYPE == type) {
253                        THE_UNSAFE.putInt(copy, offset, 0);
254                } else if (java.lang.Long.TYPE == type) {
255                        THE_UNSAFE.putLong(copy, offset, 0L);
256                } else if (java.lang.Float.TYPE == type) {
257                        THE_UNSAFE.putFloat(copy, offset, 0.0f);
258                } else if (java.lang.Double.TYPE == type) {
259                        THE_UNSAFE.putDouble(copy, offset, 0.0d);
260                }
261        }
262
263        /**
264         * Performs a deep copy of the object. With a deep copy all references from the object are also copied.
265         * The identity of referenced objects is preserved, so, for example, if the object graph contains two 
266         * references to the same object, the cloned object will preserve this structure.
267         * @param obj The object to perform a deep copy for.
268         * @param <T> The type being copied
269         * @return A deep copy of the original object.
270         */
271        public <T> T deepCopy(final T obj) {
272                return deepCopy(obj, new IdentityHashMap<Object, Object>(10));
273        }
274
275        /**
276         * Performs a deep copy of the object. With a deep copy all references from the object are also copied.
277         * The identity of referenced objects is preserved, so, for example, if the object graph contains two 
278         * references to the same object, the cloned object will preserve this structure.
279         * @param o The object to perform a deep copy for.
280         * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
281         * The key is the original object reference - the value is the copied instance for that original.
282         * @param <T> The type being copied
283         * @return A deep copy of the original object.
284         */
285        public <T> T deepCopy(final T o, IdentityHashMap<Object, Object> referencesToReuse) {
286
287                /**
288                 * To avoid unnecessary recursion and potential stackoverflow errors, we use an internal
289                 * stack
290                 */
291
292                final Deque<WorkItem<?>> stack;
293                if (referencesToReuse.size() >= REFERENCE_STACK_LIMIT) {
294                        stack = new ArrayDeque<WorkItem<?>>();
295                } else {
296                        stack = null;
297                }
298
299                Object objectInput;
300
301                WorkItem<?> nextWork = null;
302
303                while (true) {
304
305                        Object objectResult;
306
307                        if (nextWork == null) {
308                                objectInput = o;
309                        } else {
310                                objectInput = getObject(nextWork.getSource(), nextWork.getFieldAccess().fieldOffset());
311                        }
312
313                        if (objectInput == null) {
314                                objectResult = null;
315                        } else {
316
317                            if (String.class.isAssignableFrom(objectInput.getClass())) {
318                                objectInput = ((String)objectInput);
319                            }
320                            
321                                Class<?> clazz = objectInput.getClass();
322
323                                if (clazz.isPrimitive() || clazz.isEnum()) {
324                                        objectResult = objectInput;
325                                } else if (ClassUtils.isJdkImmutable(clazz) || ClassUtils.isWrapper(clazz)) {
326                                        objectResult = objectInput;
327                                } else {
328
329                                        final Object result = referencesToReuse.get(objectInput);
330                                        if (result != null) {
331                                                objectResult = result;
332                                        } else {
333                                                if (clazz.isArray()) {
334                                                        objectResult = deepCopyArray(objectInput, referencesToReuse);
335                                                } else {
336
337                                                        UnsafeClassAccess<?> classAccess = UnsafeClassAccess.get(clazz);
338                                                        objectResult = allocateInstance(objectInput.getClass());
339
340                                                        referencesToReuse.put(objectInput, objectResult);
341
342                                                        ClassAccess<?> classInHierarchy = classAccess;
343                                                        
344                                                        while (!classInHierarchy.getType().equals(java.lang.Object.class)) {
345                                                        
346                                                                for (FieldAccess<?> f : classInHierarchy.getDeclaredFieldAccessors()) {
347                                                                        
348                                                                        UnsafeFieldAccess<?> uf = (UnsafeFieldAccess<?>)f;
349                                                                        if (f.fieldClass().isPrimitive()) {
350                                                                                copyPrimitiveAtOffset(objectInput, objectResult, f.fieldClass(), uf.fieldOffset());
351                                                                        } else if (stack == null) {
352                                                                                deepCopyObjectAtOffset(objectInput, objectResult, f.fieldClass(), uf.fieldOffset(), referencesToReuse);
353                                                                        } else {
354                                                                                @SuppressWarnings({ "unchecked", "rawtypes" })
355                                                                                final WorkItem item = new WorkItem(objectInput, objectResult, uf);
356                                                                                stack.addFirst(item);
357                                                                        }
358                                                                }
359                                                        classInHierarchy = classInHierarchy.getSuperClassAccess();
360                                                        }
361                                                }
362                                        }
363                                }
364                        }
365
366                        if (nextWork == null) {
367                                nextWork = (stack == null ? null : stack.pollFirst());
368                                if (nextWork == null) {
369                                        @SuppressWarnings("unchecked")
370                                        final T convertedResult = (T) objectResult;
371                                        return convertedResult;
372                                }
373                        } else if (nextWork != null) {
374                                if (objectResult == null) {
375                                        putNullObject(nextWork.getTarget(), nextWork.getFieldAccess().fieldOffset());
376                                } else {
377                                        putObject(nextWork.getTarget(), nextWork.getFieldAccess().fieldOffset(), objectResult);
378                                }
379                                nextWork = (stack == null ? null : stack.pollFirst());
380                        }
381                }
382        }
383
384        /**
385         * Copies the object of the specified type from the given field offset in the source object 
386         * to the same location in the copy, visiting the object during the copy so that its fields are also copied
387         * @param source The object to copy from
388         * @param copy The target object
389         * @param fieldClass The declared type of object at the given offset
390         * @param offset The offset to copy from
391         */     
392        public final void deepCopyObjectAtOffset(Object source, Object copy, Class<?> fieldClass, long offset) {
393                deepCopyObjectAtOffset(source, copy, fieldClass, offset, new IdentityHashMap<Object, Object>(100));
394        }
395
396        /**
397         * Copies the object of the specified type from the given field offset in the source object 
398         * to the same location in the copy, visiting the object during the copy so that its fields are also copied
399         * @param source The object to copy from
400         * @param copy The target object
401         * @param fieldClass The declared type of object at the given offset
402         * @param offset The offset to copy from
403         * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
404         * The key is the original object reference - the value is the copied instance for that original.
405         */     
406        public final void deepCopyObjectAtOffset(Object source, Object copy, Class<?> fieldClass, long offset, IdentityHashMap<Object, Object> referencesToReuse) {
407
408                Object origFieldValue = THE_UNSAFE.getObject(source, offset);
409
410                if (origFieldValue == null) {
411
412                        putNullObject(copy, offset);
413                } else {
414
415                        final Object copyFieldValue = deepCopy(origFieldValue, referencesToReuse);
416                        UnsafeOperations.THE_UNSAFE.putObject(copy, offset, copyFieldValue);
417                }
418        }
419
420        /**
421         * Copies the object of the specified type from the given field in the source object 
422         * to the same field in the copy, visiting the object during the copy so that its fields are also copied
423         * @param source The object to copy from
424         * @param copy The target object
425         * @param field Field to be copied
426         * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
427         * The key is the original object reference - the value is the copied instance for that original.
428         */     
429        public final void deepCopyObjectField(Object source, Object copy, Field field, IdentityHashMap<Object, Object> referencesToReuse) {
430
431                deepCopyObjectAtOffset(source, copy, field.getType(), getObjectFieldOffset(field), referencesToReuse);
432        }
433
434        /**
435         * Copies the object of the specified type from the given field in the source object 
436         * to the same field in the copy, visiting the object during the copy so that its fields are also copied
437         * @param obj The object to copy from
438         * @param copy The target object
439         * @param field Field to be copied
440         */     
441        public final void deepCopyObjectField(Object obj, Object copy, Field field) {
442
443                deepCopyObjectAtOffset(obj, copy, field.getType(), getObjectFieldOffset(field), new IdentityHashMap<Object, Object>(100));
444        }
445
446        /**
447         * Copies the array of the specified type from the given field offset in the source object 
448         * to the same location in the copy, visiting the array during the copy so that its contents are also copied
449         * @param source The object to copy from
450         * @param copy The target object
451         * @param fieldClass The declared type of array at the given offset
452         * @param offset The offset to copy from
453         */     
454        public final void deepCopyArrayAtOffset(Object source, Object copy, Class<?> fieldClass, long offset) {
455                deepCopyArrayAtOffset(source, copy, fieldClass, offset, new IdentityHashMap<Object, Object>(100));
456        }
457
458        /**
459         * Copies the array of the specified type from the given field offset in the source object 
460         * to the same location in the copy, visiting the array during the copy so that its contents are also copied
461         * @param source The object to copy from
462         * @param copy The target object
463         * @param fieldClass The declared type of array at the given offset
464         * @param offset The offset to copy from
465         * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
466         * The key is the original object reference - the value is the copied instance for that original.
467         */     
468        public final void deepCopyArrayAtOffset(Object source, Object copy, Class<?> fieldClass, long offset, IdentityHashMap<Object, Object> referencesToReuse) {
469
470                Object origFieldValue = THE_UNSAFE.getObject(source, offset);
471
472                if (origFieldValue == null) {
473
474                        putNullObject(copy, offset);
475                } else {
476
477                        final Object copyFieldValue = deepCopyArray(origFieldValue, referencesToReuse);
478                        UnsafeOperations.THE_UNSAFE.putObject(copy, offset, copyFieldValue);
479                }
480        }
481
482        /**
483         * Copies the array of the specified type from the given field in the source object 
484         * to the same field in the copy, visiting the array  during the copy so that its contents are also copied
485         * @param obj The object to copy from
486         * @param copy The target object
487         * @param field Field to be copied
488         * @param referencesToReuse An identity map of references to reuse - this is further populated as the copy progresses.
489         * The key is the original object reference - the value is the copied instance for that original.
490         */     
491        public final void deepCopyArrayField(Object obj, Object copy, Field field, IdentityHashMap<Object, Object> referencesToReuse) {
492
493                deepCopyArrayAtOffset(obj, copy, field.getType(), getObjectFieldOffset(field), referencesToReuse);
494        }
495
496        /**
497         * Copies the array of the specified type from the given field in the source object 
498         * to the same field in the copy, visiting the array  during the copy so that its contents are also copied
499         * @param obj The object to copy from
500         * @param copy The target object
501         * @param field Field to be copied
502         */     
503        public final void deepCopyArrayField(Object obj, Object copy, Field field) {
504
505                deepCopyArrayAtOffset(obj, copy, field.getType(), getObjectFieldOffset(field), new IdentityHashMap<Object, Object>(100));
506        }
507
508        /**
509         * Performs a deep copy of the array. With a deep copy all references from the array are also copied.
510         * The identity of referenced objects is preserved, so, for example, if the object graph contains two 
511         * references to the same object, the cloned object will preserve this structure.
512         * @param arrayOriginal The array to perform a deep copy for.
513         * @param visited An identity map of references to reuse - this is further populated as the copy progresses.
514         * The key is the original object reference - the value is the copied instance for that original.
515         * @return A deep copy of the original array.
516         */
517        public final Object deepCopyArray(Object arrayOriginal, IdentityHashMap<Object, Object> visited) {
518
519                if (visited.containsKey(arrayOriginal)) {
520                        return visited.get(arrayOriginal);
521                }
522
523                final Class<?> componentType = arrayOriginal.getClass().getComponentType();
524
525                Object result = null;
526
527                if (componentType.isPrimitive()) {
528
529                        if (java.lang.Boolean.TYPE == componentType) {
530                                result = Arrays.copyOf((boolean[]) arrayOriginal, ((boolean[]) arrayOriginal).length);
531                        } else if (java.lang.Byte.TYPE == componentType) {
532                                result = Arrays.copyOf((byte[]) arrayOriginal, ((byte[]) arrayOriginal).length);
533                        } else if (java.lang.Character.TYPE == componentType) {
534                                result = Arrays.copyOf((char[]) arrayOriginal, ((char[]) arrayOriginal).length);
535                        } else if (java.lang.Short.TYPE == componentType) {
536                                result = Arrays.copyOf((short[]) arrayOriginal, ((short[]) arrayOriginal).length);
537                        } else if (java.lang.Integer.TYPE == componentType) {
538                                result = Arrays.copyOf((int[]) arrayOriginal, ((int[]) arrayOriginal).length);
539                        } else if (java.lang.Long.TYPE == componentType) {
540                                result = Arrays.copyOf((long[]) arrayOriginal, ((long[]) arrayOriginal).length);
541                        } else if (java.lang.Float.TYPE == componentType) {
542                                result = Arrays.copyOf((float[]) arrayOriginal, ((float[]) arrayOriginal).length);
543                        } else if (java.lang.Double.TYPE == componentType) {
544                                result = Arrays.copyOf((double[]) arrayOriginal, ((double[]) arrayOriginal).length);
545                        }
546                }
547
548                if (result == null) {
549                        Object[] arrayCopy = Arrays.copyOf((Object[]) arrayOriginal, ((Object[]) arrayOriginal).length);
550                        if (arrayCopy.length > 0) {
551
552                                if (componentType.isArray()) {
553                                        for (int i = 0; i < arrayCopy.length; i++) {
554                                                arrayCopy[i] = deepCopyArray(arrayCopy[i], visited);
555                                        }
556                                } else {
557                                        for (int i = 0; i < arrayCopy.length; i++) {
558                                                Object component = deepCopy(arrayCopy[i], visited);
559                                                arrayCopy[i] = component;
560                                        }
561                                }
562                        }
563                        result = arrayCopy;
564                }
565
566                visited.put(arrayOriginal, result);
567                return result;
568        }
569
570        /**
571         * Determines the shallow memory size of an instance of the given class
572         * @param clazz The class to calculate the shallow size for
573         * @return Size in bytes
574         */
575        public final long shallowSizeOf(Class<?> clazz) {
576                return doShallowSizeOfClass(clazz);
577        }
578
579        /**
580         * Determines the shallow memory size of the given object (object or array)
581         * @param obj The object instance to calculate the shallow size for
582         * @return Size in bytes
583         */
584        public final long shallowSizeOf(Object obj) {
585
586                if (obj == null) {
587                        return 0;
588                }
589
590                if (obj.getClass().isArray()) {
591                        return doShallowSizeOfArray(obj);
592                } else {
593                        return doShallowSizeOfClass(obj.getClass());
594                }
595        }
596
597        private long doShallowSizeOfArray(Object array) {
598
599                long size = getSizeOfArrayHeader();
600                final int length = Array.getLength(array);
601                if (length > 0) {
602
603                        Class<?> type = array.getClass().getComponentType();
604                        if (type.isPrimitive()) {
605
606                                if (java.lang.Boolean.TYPE == type) {
607                                        size = size + (length * SIZE_BYTES_BOOLEAN);
608                                } else if (java.lang.Byte.TYPE == type) {
609                                        size = size + (length * SIZE_BYTES_BYTE);
610                                } else if (java.lang.Character.TYPE == type) {
611                                        size = size + (length * SIZE_BYTES_CHAR);
612                                } else if (java.lang.Short.TYPE == type) {
613                                        size = size + (length * SIZE_BYTES_SHORT);
614                                } else if (java.lang.Integer.TYPE == type) {
615                                        size = size + (length * SIZE_BYTES_INT);
616                                } else if (java.lang.Long.TYPE == type) {
617                                        size = size + (length * SIZE_BYTES_LONG);
618                                } else if (java.lang.Float.TYPE == type) {
619                                        size = size + (length * SIZE_BYTES_FLOAT);
620                                } else if (java.lang.Double.TYPE == type) {
621                                        size = size + (length * SIZE_BYTES_DOUBLE);
622                                }
623
624                        } else {
625                                size = size + (length * getSizeOfObjectHeader());
626                        }
627                }
628
629                size = size + SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT - 1L;
630                return size - (size % SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT);
631        }
632
633        private long doShallowSizeOfClass(Class<?> clazz) {
634
635                if (clazz.isArray()) {
636                        throw new IllegalArgumentException("Shallow Size of cannot be calculated for arrays classes as component length is needed");
637                }
638                if (clazz.isPrimitive()) {
639
640                        return getSizeForPrimitive(clazz);
641                }
642                if (clazz == Object.class) {
643                        return MIN_SIZE;
644                }
645
646                long size = getSizeOfObjectHeader();
647
648                Field[] fields = ClassUtils.collectInstanceFields(clazz);
649
650                for (Field f : fields) {
651
652                        Class<?> fieldClass = f.getType();
653                        final int fieldSize = fieldClass.isPrimitive() ? getSizeForPrimitive(fieldClass) : getSizeOfObjectHeader();
654                        final long offsetPlusSize = getObjectFieldOffset(f) + fieldSize;
655
656                        if (offsetPlusSize > size) {
657                                size = offsetPlusSize;
658                        }
659                }
660
661                size = size + SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT - 1L;
662                return size - (size % SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT);
663        }
664
665        /**
666         * Determines the deep memory size of the given object (object or array), visiting all its references
667         * @param o The object instance to calculate the deep size for
668         * @return Size in bytes
669         */
670        public final long deepSizeOf(Object o) {
671
672                IdentityHashMap<Object, Boolean> seenObjects = new IdentityHashMap<Object, Boolean>(10);
673                return doDeepSizeOf(o, seenObjects);
674        }
675
676        private long doDeepSizeOf(Object o, IdentityHashMap<Object, Boolean> seenObjects) {
677
678                if (o == null) {
679                        return 0;
680                }
681
682                Class<?> clazz = o.getClass();
683                if (clazz.isPrimitive()) {
684                        return getSizeForPrimitive(clazz);
685                }
686
687                seenObjects.put(o, Boolean.TRUE);
688
689                if (clazz.isArray()) {
690
691                        long size = doShallowSizeOfArray(o);
692
693                        if (!clazz.getComponentType().isPrimitive()) {
694
695                                Object[] array = (Object[]) o;
696                                for (int i = 0; i < array.length; i++) {
697                                        Object nextObject = array[i];
698                                        if (nextObject != null && !seenObjects.containsKey(nextObject)) {
699                                                size = size + doDeepSizeOf(nextObject, seenObjects);
700                                        }
701                                }
702                        }
703
704                        return size;
705
706                } else {
707
708                        if (clazz == Object.class) {
709                                return MIN_SIZE;
710                        }
711
712                        long size = getSizeOfObjectHeader();
713                        long additionalSize = 0;
714
715                        Field[] fields = ClassUtils.collectInstanceFields(clazz);
716
717                        for (Field f : fields) {
718
719                                long objectFieldOffset = getObjectFieldOffset(f);
720
721                                Class<?> fieldClass = f.getType();
722                                final int fieldSize = fieldClass.isPrimitive() ? getSizeForPrimitive(fieldClass) : getSizeOfObjectHeader();
723                                final long offsetPlusSize = objectFieldOffset + fieldSize;
724
725                                if (offsetPlusSize > size) {
726                                        size = offsetPlusSize;
727                                }
728
729                                if (!fieldClass.isPrimitive()) {
730                                        Object fieldObject = getObject(o, objectFieldOffset);
731                                        if (fieldObject != null && !seenObjects.containsKey(fieldObject)) {
732                                                additionalSize += doDeepSizeOf(fieldObject, seenObjects);
733                                        }
734                                }
735                        }
736
737                        size = size + SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT - 1L;
738                        size = size - (size % SIZE_BYTES_PAGE_FOR_OBJECT_ALIGNMENT);
739
740                        return size + additionalSize;
741                }
742        }
743
744        /**
745         * Memory address payload is an unsigned value - we need to normalise the 'sign' to get a
746         * meaningful value back - when we do this we need to store it into a long
747         * @param value The value to normalise
748         * @return The normalised value as a long
749         */
750        private static long normalize(int value) {
751                if (value >= 0) {
752                        return value;
753                }
754                return (~0L >>> 32) & value;
755        }
756
757        /**
758         * Get the size in bytes of a native pointer - either 4 or 8 (for 32-bit or 64-bit JRE).
759         * For primitive types, the size is determined by their data type rather than this value.
760         * @return The address size
761         */
762        public final int getAddressSize() {
763                return THE_UNSAFE.addressSize();
764        }       
765        
766        /**
767         * Retrieve the object at the given offset of the supplied parent object
768         * @param parent The Object's parent
769         * @param offset The offset
770         * @return The retrieved object
771         */
772        public final Object getObject(Object parent, long offset) {
773                return THE_UNSAFE.getObject(parent, offset);
774        }
775
776        /**
777         * Retrieve the value at the given offset of the supplied parent object
778         * @param parent The Object's parent
779         * @param offset The offset
780         * @return The retrieved boolean
781         */
782        public final boolean getBoolean(Object parent, long offset) {
783                return THE_UNSAFE.getBoolean(parent, offset);
784        }
785
786        /**
787         * Retrieve the value at the given offset of the supplied parent object
788         * @param parent The Object's parent
789         * @param offset The offset
790         * @return The retrieved char
791         */
792        public final char getChar(Object parent, long offset) {
793                return THE_UNSAFE.getChar(parent, offset);
794        }
795        
796        /**
797         * Retrieve the value at the given offset of the supplied parent object
798         * @param parent The Object's parent
799         * @param offset The offset
800         * @return The retrieved short
801         */
802        public final short getShort(Object parent, long offset) {
803                return THE_UNSAFE.getShort(parent, offset);
804        }
805        
806        /**
807         * Retrieve the value at the given offset of the supplied parent object
808         * @param parent The Object's parent
809         * @param offset The offset
810         * @return The retrieved int
811         */
812        public final int getInt(Object parent, long offset) {
813                return THE_UNSAFE.getInt(parent, offset);
814        }
815        
816        /**
817         * Retrieve the value at the given offset of the supplied parent object
818         * @param parent The Object's parent
819         * @param offset The offset
820         * @return The retrieved long
821         */
822        public final long getLong(Object parent, long offset) {
823                return THE_UNSAFE.getLong(parent, offset);
824        }
825        
826        /**
827         * Retrieve the value at the given offset of the supplied parent object
828         * @param parent The Object's parent
829         * @param offset The offset
830         * @return The retrieved float
831         */
832        public final float getFloat(Object parent, long offset) {
833                return THE_UNSAFE.getFloat(parent, offset);
834        }
835        
836        /**
837         * Retrieve the value at the given offset of the supplied parent object
838         * @param parent The Object's parent
839         * @param offset The offset
840         * @return The retrieved double
841         */
842        public final double getDouble(Object parent, long offset) {
843                return THE_UNSAFE.getDouble(parent, offset);
844        }
845
846        /**
847         * Retrieve the value at the given offset of the supplied parent object
848         * @param parent The Object's parent
849         * @param offset The offset
850         * @return The retrieved byte
851         */
852        public final byte getByte(Object parent, long offset) {
853                return THE_UNSAFE.getByte(parent, offset);
854        }
855
856        /**
857         * Write a null value to the given offset of the supplied parent object
858         * @param parent The Object's parent
859         * @param offset The offset
860         */
861        public final void putNullObject(Object parent, long offset) {
862                THE_UNSAFE.putObject(parent, offset, null);
863        }
864
865        /**
866         * Resets to false the boolean value at the given offset of the supplied parent object
867         * @param parent The Object's parent
868         * @param offset The offset
869         */
870        public final void putDefaultBoolean(Object parent, long offset) {
871                THE_UNSAFE.putBoolean(parent, offset, false);
872        }
873
874        /**
875         * Resets to u0000 the char value at the given offset of the supplied parent object
876         * @param parent The Object's parent
877         * @param offset The offset
878         */
879        public final void putDefaultChar(Object parent, long offset) {
880                THE_UNSAFE.putChar(parent, offset, '\u0000');
881        }
882        
883        /**
884         * Resets to 0 the short value at the given offset of the supplied parent object
885         * @param parent The Object's parent
886         * @param offset The offset
887         */
888        public final void putDefaultShort(Object parent, long offset) {
889                THE_UNSAFE.putShort(parent, offset, (short)0);
890        }
891
892        /**
893         * Resets to 0 the int value at the given offset of the supplied parent object
894         * @param parent The Object's parent
895         * @param offset The offset
896         */
897        public final void putDefaultInt(Object parent, long offset) {
898                THE_UNSAFE.putInt(parent, offset, 0);
899        }
900        
901        /**
902         * Resets to 0 the long value at the given offset of the supplied parent object
903         * @param parent The Object's parent
904         * @param offset The offset
905         */
906        public final void putDefaultLong(Object parent, long offset) {
907                THE_UNSAFE.putLong(parent, offset, 0L);
908        }
909        
910        /**
911         * Resets to 0.0f the float value at the given offset of the supplied parent object
912         * @param parent The Object's parent
913         * @param offset The offset
914         */
915        public final void putDefaultFloat(Object parent, long offset) {
916                THE_UNSAFE.putFloat(parent, offset, 0.0F);
917        }
918        
919        /**
920         * Resets to 0.0d the double value at the given offset of the supplied parent object
921         * @param parent The Object's parent
922         * @param offset The offset
923         */
924        public final void putDefaultDouble(Object parent, long offset) {
925                THE_UNSAFE.putDouble(parent, offset, 0.0D);
926        }
927        
928        /**
929         * Resets to 0 the byte value at the given offset of the supplied parent object
930         * @param parent The Object's parent
931         * @param offset The offset
932         */
933        public final void putDefaultByte(Object parent, long offset) {
934                THE_UNSAFE.putByte(parent, offset, (byte)0);
935        }
936
937        /**
938         * Puts the object's reference at the given offset of the supplied parent object
939         * @param parent The Object's parent
940         * @param offset The offset
941         * @param value Object to be put
942         */
943        public final void putObject(Object parent, long offset, Object value) {
944                THE_UNSAFE.putObject(parent, offset, value);
945        }
946
947        /**
948         * Puts the value at the given offset of the supplied parent object
949         * @param parent The Object's parent
950         * @param offset The offset
951         * @param value boolean to be put
952         */
953        public final void putBoolean(Object parent, long offset, boolean value) {
954                THE_UNSAFE.putBoolean(parent, offset, value);
955        }
956
957        /**
958         * Puts the value at the given offset of the supplied parent object
959         * @param parent The Object's parent
960         * @param offset The offset
961         * @param value char to be put
962         */
963        public final void putChar(Object parent, long offset, char value) {
964                THE_UNSAFE.putChar(parent, offset, value);
965        }
966        
967        /**
968         * Puts the value at the given offset of the supplied parent object
969         * @param parent The Object's parent
970         * @param offset The offset
971         * @param value short to be put
972         */
973        public final void putShort(Object parent, long offset, short value) {
974                THE_UNSAFE.putShort(parent, offset, value);
975        }
976        
977        /**
978         * Puts the value at the given offset of the supplied parent object
979         * @param parent The Object's parent
980         * @param offset The offset
981         * @param value int to be put
982         */
983        public final void putInt(Object parent, long offset, int value) {
984                THE_UNSAFE.putInt(parent, offset, value);
985        }
986        
987        /**
988         * Puts the value at the given offset of the supplied parent object
989         * @param parent The Object's parent
990         * @param offset The offset
991         * @param value long to be put
992         */
993        public final void putLong(Object parent, long offset, long value) {
994                THE_UNSAFE.putLong(parent, offset, value);
995        }
996        
997        /**
998         * Puts the value at the given offset of the supplied parent object
999         * @param parent The Object's parent
1000         * @param offset The offset
1001         * @param value float to be put
1002         */
1003        public final void putFloat(Object parent, long offset, float value) {
1004                THE_UNSAFE.putFloat(parent, offset, value);
1005        }
1006        
1007        /**
1008         * Puts the value at the given offset of the supplied parent object
1009         * @param parent The Object's parent
1010         * @param offset The offset
1011         * @param value double to be put
1012         */
1013        public final void putDouble(Object parent, long offset, double value) {
1014                THE_UNSAFE.putDouble(parent, offset, value);
1015        }
1016        
1017        /**
1018         * Puts the value at the given offset of the supplied parent object
1019         * @param parent The Object's parent
1020         * @param offset The offset
1021         * @param value byte to be put
1022         */
1023        public final void putByte(Object parent, long offset, byte value) {
1024                THE_UNSAFE.putByte(parent, offset, value);
1025        }
1026        
1027        private class WorkItem<P> {
1028
1029                private final Object source;
1030                private final Object target;
1031                private final UnsafeFieldAccess<P> fieldAccess;
1032
1033                public WorkItem(Object source, Object target, UnsafeFieldAccess<P> fieldAccess) {
1034                        this.source = source;
1035                        this.target = target;
1036                        this.fieldAccess = fieldAccess;
1037                }
1038
1039                public Object getSource() {
1040                        return source;
1041                }
1042
1043                public Object getTarget() {
1044                        return target;
1045                }
1046
1047                public UnsafeFieldAccess<P> getFieldAccess() {
1048                        return fieldAccess;
1049                }
1050        }
1051
1052        @SuppressWarnings("unused")
1053        private static final class SingleFieldHolder {
1054                public int field;
1055        }
1056
1057        private final int getSizeOfArrayHeader() {
1058                return THE_UNSAFE.arrayBaseOffset(byte[].class);
1059        }
1060
1061        private final int getSizeForPrimitive(Class<?> clazz) {
1062
1063                if (java.lang.Boolean.TYPE.isAssignableFrom(clazz)) {
1064                        return SIZE_BYTES_BOOLEAN;
1065                } else if (java.lang.Byte.TYPE.isAssignableFrom(clazz)) {
1066                        return SIZE_BYTES_BYTE;
1067                } else if (java.lang.Character.TYPE.isAssignableFrom(clazz)) {
1068                        return SIZE_BYTES_CHAR;
1069                } else if (java.lang.Short.TYPE.isAssignableFrom(clazz)) {
1070                        return SIZE_BYTES_SHORT;
1071                } else if (java.lang.Integer.TYPE.isAssignableFrom(clazz)) {
1072                        return SIZE_BYTES_INT;
1073                } else if (java.lang.Long.TYPE.isAssignableFrom(clazz)) {
1074                        return SIZE_BYTES_LONG;
1075                } else if (java.lang.Float.TYPE.isAssignableFrom(clazz)) {
1076                        return SIZE_BYTES_FLOAT;
1077                } else if (java.lang.Double.TYPE.isAssignableFrom(clazz)) {
1078                        return SIZE_BYTES_DOUBLE;
1079                }
1080                throw new IllegalArgumentException("Class " + clazz.getName() + " is not primitive");
1081        }
1082
1083        private final int getSizeOfObjectHeader() {
1084                try {
1085                        return (int) THE_UNSAFE.objectFieldOffset(SingleFieldHolder.class.getDeclaredField("field"));
1086                } catch (NoSuchFieldException e) {
1087                        throw new IllegalStateException("Cannot determine size of object header", e);
1088                } catch (SecurityException e) {
1089                        throw new IllegalStateException("Cannot determine size of object header", e);
1090                }
1091        }
1092}