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}