| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. You may obtain a |
| * copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package org.apache.geode.internal.size; |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| |
| import org.apache.geode.annotations.Immutable; |
| import org.apache.geode.internal.Assert; |
| import org.apache.geode.internal.JvmSizeUtils; |
| import org.apache.geode.unsafe.internal.sun.misc.Unsafe; |
| |
| /** |
| * Figure out the size of an object using reflection. This class does not follow any object |
| * references, it just calculates the size of a flat object. |
| * |
| * |
| */ |
| public class ReflectionSingleObjectSizer implements SingleObjectSizer { |
| public static final int REFERENCE_SIZE = JvmSizeUtils.getReferenceSize(); |
| public static final int OBJECT_SIZE = JvmSizeUtils.getObjectHeaderSize(); |
| |
| @Immutable |
| private static final Unsafe unsafe; |
| static { |
| Unsafe tmp = null; |
| try { |
| tmp = new Unsafe(); |
| } catch (RuntimeException ignore) { |
| } catch (Error ignore) { |
| } |
| unsafe = tmp; |
| } |
| |
| @Override |
| public long sizeof(Object object) { |
| return sizeof(object, true); |
| } |
| |
| public long sizeof(Object object, boolean roundResult) { |
| Class<?> clazz = object.getClass(); |
| long size; |
| if (clazz.isArray()) { |
| if (unsafe != null) { |
| size = unsafe.arrayBaseOffset(clazz); |
| int arrayLength = Array.getLength(object); |
| if (arrayLength > 0) { |
| int typeSize = unsafe.arrayScaleIndex(clazz); |
| if (typeSize == 0) { |
| // the javadocs say that arrayScaleIndex may return 0. |
| // If it did then we use sizeType. |
| typeSize = sizeType(clazz.getComponentType()); |
| } |
| size += (long) arrayLength * typeSize; |
| } |
| } else { |
| // not as accurate but does not use unsafe |
| size = OBJECT_SIZE + 4 /* for array length */; |
| int arrayLength = Array.getLength(object); |
| if (arrayLength > 0) { |
| size += (long) arrayLength * sizeType(clazz.getComponentType()); |
| } |
| } |
| if (roundResult) { |
| size = roundUpSize(size); |
| } |
| return size; |
| } else { |
| return sizeof(clazz, roundResult); |
| } |
| } |
| |
| public static long sizeof(Class clazz) { |
| return sizeof(clazz, true); |
| } |
| |
| /** |
| * Since unsafe.fieldOffset(Field) will give us the offset to the first byte of that field all we |
| * need to do is find which of the non-static declared fields has the greatest offset. |
| */ |
| public static long sizeof(Class clazz, boolean roundResult) { |
| Assert.assertTrue(!clazz.isArray()); |
| long size; |
| if (unsafe != null) { |
| Field lastField = null; |
| long lastFieldOffset = 0; |
| do { |
| Field[] fields = clazz.getDeclaredFields(); |
| for (Field field : fields) { |
| if (!Modifier.isStatic(field.getModifiers())) { |
| long offset = unsafe.fieldOffset(field); |
| if (offset >= lastFieldOffset) { |
| lastFieldOffset = offset; |
| lastField = field; |
| } |
| } |
| } |
| if (lastField != null) { |
| // if we have found a field in a subclass then one of them will be the last field |
| // so just break without looking at super class fields. |
| break; |
| } |
| clazz = clazz.getSuperclass(); |
| } while (clazz != null); |
| |
| if (lastField != null) { |
| size = lastFieldOffset + sizeType(lastField.getType()); |
| } else { |
| // class with no fields |
| size = OBJECT_SIZE; |
| } |
| } else { |
| // This code is not as accurate as unsafe but gives an estimate of memory used. |
| // If it is wrong it will always under estimate because it does not account |
| // for any of the field alignment that the jvm does. |
| size = OBJECT_SIZE; |
| do { |
| Field[] fields = clazz.getDeclaredFields(); |
| for (Field field : fields) { |
| if (!Modifier.isStatic(field.getModifiers())) { |
| size += sizeType(field.getType()); |
| } |
| } |
| clazz = clazz.getSuperclass(); |
| } while (clazz != null); |
| } |
| if (roundResult) { |
| size = roundUpSize(size); |
| } |
| return size; |
| } |
| |
| public static long roundUpSize(long size) { |
| // Round up to the nearest 8 bytes. Experimentally, this |
| // is what we've seen the sun 32 bit VM do with object size. |
| // See https://wiki.gemstone.com/display/rusage/Per+Entry+Overhead |
| long remainder = size % 8; |
| if (remainder != 0) { |
| size = size - remainder + 8; |
| } |
| return size; |
| } |
| |
| private static int sizeType(Class<?> t) { |
| |
| if (t == Boolean.TYPE) |
| return 1; |
| else if (t == Byte.TYPE) |
| return 1; |
| else if (t == Character.TYPE) |
| return 2; |
| else if (t == Short.TYPE) |
| return 2; |
| else if (t == Integer.TYPE) |
| return 4; |
| else if (t == Long.TYPE) |
| return 8; |
| else if (t == Float.TYPE) |
| return 4; |
| else if (t == Double.TYPE) |
| return 8; |
| else if (t == Void.TYPE) |
| return 0; |
| else |
| return REFERENCE_SIZE; |
| } |
| |
| |
| } |