blob: 03240b527d452fc1ee1bae981559f7e30922997f [file] [log] [blame]
/*
* 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;
}
}