来源
关于模拟jdk.nashorn.internal.ir.debug.ObjectSizeCalculator.getObjectSize(Object o)获取java对象大小
使用估计内存计算的方式对比ObjectSizeCalculator.getObjectSize()方法大小
参考:https://www.cnblogs.com/shown/p/6211179.html
不想搞agent就想直接获取;然而有的jdk或高版本的jdk已经没有ObjectSizeCalculator.getObjectSize()这个方法了,记录一下这种内存的方式:
结论
import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
import java.util.HashMap;
import java.util.Map;
public class ObjectSizeCalculatorCom {
public static void main(String[] args) throws Exception {
// 创建两个对象验证
String myObject = "Hello, world!";
Map<String, String> map = new HashMap<String, String>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
// 使用ObjectSizeCalculator计算对象大小
long size1 = ObjectSizeCalculator.getObjectSize(map);
System.out.println("Size with ObjectSizeCalculator: " + size1);
// 内存方式对比结果
System.out.println("Size with memory: " + calcSize(map));
}
/**
* 计算缓存大小.
* @param object
* @return
*/
private static long calcSize(Object object) {
ObjectMemoryCalculationInfo objectInfo;
try {
objectInfo = new ObjectMemoryCalculator().calculateMemory(object);
} catch (IllegalAccessException e) {
return 0;
}
return objectInfo.calculateDeepSize();
}
}
输出结果
//String
Size with ObjectSizeCalculator: 72
Size with memory: 72
//map
Size with ObjectSizeCalculator: 536
Size with memory: 544
参考代码
import java.util.*;
/**
* 对象内存计算信息类,用于计算对象及其内存布局的信息。
*
* @author HH287
* @version 1.0.0 2024/4/8 17:25
* @since JDK 1.8.0
*/
public class ObjectMemoryCalculationInfo {
public final int arrayElementSize; // 数组元素大小
public final int arraySize; // 数组大小
public final List<ObjectMemoryCalculationInfo> childObjects; // 子对象列表
public final int objectOffset; // 对象偏移量
public final int objectLength; // 对象长度
public final int arrayBaseAddress; // 数组基地址
long totalSize = 0L; // 总大小
/**
* 构造函数,初始化对象内存计算信息。
*
* @param name 对象名称
* @param type 对象类型
* @param contents 对象内容
* @param objectOffset 对象偏移量
* @param objectLength 对象长度
* @param arraySize 数组大小
* @param arrayBaseAddress 数组基地址
* @param arrayElementSize 数组元素大小
*/
public ObjectMemoryCalculationInfo(String name, String type, String contents, int objectOffset, int objectLength, int arraySize, int arrayBaseAddress, int arrayElementSize) {
this.objectOffset = objectOffset;
this.objectLength = objectLength;
this.arraySize = arraySize;
this.arrayBaseAddress = arrayBaseAddress;
this.arrayElementSize = arrayElementSize;
this.childObjects = new ArrayList<>(1);
}
/**
* 添加子对象。
*
* @param childObject 子对象的内存计算信息
*/
public void addChild(ObjectMemoryCalculationInfo childObject) {
if (childObject != null) {
this.childObjects.add(childObject);
}
}
/**
* 计算对象及其子对象的深度大小。
*
* @return 对象的深度大小
*/
public long calculateDeepSize() {
return this.addPaddingSize((long)this.arraySize + this.calculateUnderlyingSize(this.arraySize != 0));
}
/**
* 计算对象及其子对象的底层大小。
*
* @param isArray 是否为数组
* @return 底层大小
*/
private long calculateUnderlyingSize(boolean isArray) {
// 遍历子对象,累加它们的大小
ObjectMemoryCalculationInfo child;
for (Iterator<ObjectMemoryCalculationInfo> iterator = this.childObjects.iterator(); iterator.hasNext(); this.totalSize += (long)child.arraySize + child.calculateUnderlyingSize(child.arraySize != 0)) {
child = iterator.next();
}
// 如果不是数组且子对象不为空,则对最后一个子对象的末尾添加对齐填充
if (!isArray && !this.childObjects.isEmpty()) {
int lastChildEnd = ((ObjectMemoryCalculationInfo)this.childObjects.get(this.childObjects.size() - 1)).objectOffset + ((ObjectMemoryCalculationInfo)this.childObjects.get(this.childObjects.size() - 1)).objectLength;
this.totalSize += this.addPaddingSize((long)lastChildEnd);
}
return this.totalSize;
}
/**
* 根据偏移量对子对象进行排序。
*/
public void sortChildrenByOffset() {
this.childObjects.sort(new OffsetComparator());
}
/**
* 生成对象及其子对象的字符串表示。
*
* @return 对象的字符串表示
*/
public String toString() {
StringBuilder sb = new StringBuilder();
this.buildToStringHelper(sb, 0);
return sb.toString();
}
/**
* 辅助方法,生成对象及其子对象的字符串表示。
*
* @param sb 字符串构建器
* @param depth 递归深度
*/
private void buildToStringHelper(StringBuilder sb, int depth) {
this.indent(sb, depth).append(", objectOffset=").append(this.objectOffset).append(", objectLength=").append(this.objectLength);
if (this.arraySize > 0) {
sb.append(", arrayBaseAddress=").append(this.arrayBaseAddress);
sb.append(", arrayElementSize=").append(this.arrayElementSize);
sb.append(", arraySize=").append(this.arraySize);
}
// 遍历并递归生成子对象的字符串表示
Iterator<ObjectMemoryCalculationInfo> childIterator = this.childObjects.iterator();
while (childIterator.hasNext()) {
ObjectMemoryCalculationInfo child = childIterator.next();
sb.append('\n');
child.buildToStringHelper(sb, depth + 1);
}
}
/**
* 在字符串构建器中添加缩进。
*
* @param sb 字符串构建器
* @param depth 递归深度
* @return 缩进后的字符串构建器
*/
private StringBuilder indent(StringBuilder sb, int depth) {
for (int i = 0; i < depth; ++i) {
sb.append("\t");
}
return sb;
}
/**
* 计算并添加对齐填充大小。
*
* @param size 原始大小
* @return 添加对齐填充后的大小
*/
private long addPaddingSize(long size) {
return size % 8L != 0L ? (size / 8L + 1L) * 8L : size;
}
/**
* 内部类,用于偏移量的比较,实现Comparator接口。
*/
private static final class OffsetComparator implements Comparator<ObjectMemoryCalculationInfo> {
private OffsetComparator() {
}
/**
* 比较两个对象的偏移量。
*
* @param o1 对象1
* @param o2 对象2
* @return 偏移量的差值
*/
public int compare(ObjectMemoryCalculationInfo o1, ObjectMemoryCalculationInfo o2) {
return o1.objectOffset - o2.objectOffset;
}
}
}
import sun.misc.Unsafe;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;
/**
* ObjectMemoryCalculator 是一个用于计算 Java 对象内存占用情况的工具类。它利用反射和 `sun.misc.Unsafe` 类,
* 能够递归遍历对象及其内部字段,计算出对象的浅层大小、数组大小、偏移量以及索引比例等信息。
* 主要功能包括:
* - 计算单个对象的内存占用(包括基本类型和引用类型)
* - 处理对象数组及其内容的内存占用
* - 遍历对象树结构,处理嵌套对象和字段
* - 防止递归循环引用导致的无限循环
* - 支持指定最大递归深度限制
* @author HH287 <br>
* @version 1.0.0 2024/4/8 17:25 <br>
* @since JDK 1.8.0
*/
public class ObjectMemoryCalculator {
private static final Unsafe unsafe;
private static final Map<Class, Integer> primitiveSizes;
private static final int objectRefSize;
private IdentityHashMap<Object, Boolean> visitedObjects = new IdentityHashMap(100);
/**
* 构造函数。
*/
public ObjectMemoryCalculator() {
}
/**
* 递归计算对象的内存占用。
*
* @param obj 当前要计算内存的对象。
* @param field 当前对象的字段,用于计算递归深度和字段信息。
* @param depth 当前递归深度。
* @return 对象内存占用的详细信息。
* @throws IllegalAccessException 如果访问对象的字段时发生错误。
*/
private ObjectMemoryCalculationInfo calculateMemory(Object obj, Field field, int depth) throws IllegalAccessException {
boolean isPrimitive = field != null && field.getType().isPrimitive(); // 判断字段是否为基本类型
boolean isRecursive = false;
if (!isPrimitive) {
if (this.visitedObjects.containsKey(obj)) {
isRecursive = true; // 检测循环引用
}
this.visitedObjects.put(obj, true);
}
Class type = field != null && (obj == null || isPrimitive) ? field.getType() : obj.getClass(); // 获取对象或字段类型
int arraySize = 0;
int baseOffset = 0;
int indexScale = 0;
if (type.isArray() && obj != null) { // 处理数组类型
baseOffset = unsafe.arrayBaseOffset(type);
indexScale = unsafe.arrayIndexScale(type);
arraySize = baseOffset + indexScale * Array.getLength(obj);
}
ObjectMemoryCalculationInfo root;
int nextDepth;
if (field == null) {
root = new ObjectMemoryCalculationInfo("", type.getCanonicalName(), getContents(obj, type), 0, getShallowSize(type), arraySize, baseOffset, indexScale);
} else {
nextDepth = (int)unsafe.objectFieldOffset(field);
root = new ObjectMemoryCalculationInfo(field.getName(), type.getCanonicalName(), getContents(obj, type), nextDepth, getShallowSize(type), arraySize, baseOffset, indexScale);
}
nextDepth = depth + 1;
if (!isRecursive && obj != null && nextDepth < 10 && !type.getName().startsWith("sun.")) { // 避免递归过深和遍历Java内部类
if (isObjectArray(type)) { // 处理对象数组
Object[] array = (Object[])((Object[])obj);
Object[] tempArray = array;
int length = array.length;
for(int i = 0; i < length; ++i) {
Object item = tempArray[i];
if (item != null) {
root.addChild(this.calculateMemory(item, (Field)null, nextDepth));
}
}
} else {
// 遍历并计算对象所有非静态字段的内存占用
Iterator iterator = getAllFields(type).iterator();
while(iterator.hasNext()) {
Field tempField = (Field)iterator.next();
if ((tempField.getModifiers() & 8) == 0) {
tempField.setAccessible(true);
root.addChild(this.calculateMemory(tempField.get(obj), tempField, nextDepth));
}
}
}
}
root.sortChildrenByOffset(); // 根据字段偏移量排序子对象
return root;
}
/**
* 获取类的所有字段,包括继承自父类的字段。
*
* @param type 要获取字段的类。
* @return 类中所有字段的列表。
*/
private static List<Field> getAllFields(Class type) {
if (type.isPrimitive()) {
return Collections.emptyList();
} else {
Class current = type;
List<Field> result = new ArrayList(10);
while(true) {
Collections.addAll(result, current.getDeclaredFields());
if (current == Object.class) {
return result;
}
current = current.getSuperclass();
}
}
}
/**
* 判断给定类型是否为对象数组。
*
* @param type 类型。
* @return 如果给定类型是对象数组,则返回true,否则返回false。
*/
private static boolean isObjectArray(Class type) {
if (!type.isArray()) {
return false;
} else {
return type != byte[].class && type != boolean[].class && type != char[].class && type != short[].class && type != int[].class && type != long[].class && type != float[].class && type != double[].class;
}
}
/**
* 根据对象值和类型,返回对象内容的字符串表示。
*
* @param value 对象值。
* @param type 对象类型。
* @return 对象内容的字符串表示。
*/
private static String getContents(Object value, Class type) {
if (value == null) {
return "null";
} else if (type.isArray()) {
// 根据不同的数组类型,返回不同的字符串表示
if (type == byte[].class) {
return Arrays.toString((byte[])((byte[])value));
} else if (type == boolean[].class) {
return Arrays.toString((boolean[])((boolean[])value));
} else if (type == char[].class) {
return Arrays.toString((char[])((char[])value));
} else if (type == short[].class) {
return Arrays.toString((short[])((short[])value));
} else if (type == int[].class) {
return Arrays.toString((int[])((int[])value));
} else if (type == long[].class) {
return Arrays.toString((long[])((long[])value));
} else if (type == float[].class) {
return Arrays.toString((float[])((float[])value));
} else {
return type == double[].class ? Arrays.toString((double[])((double[])value)) : Arrays.toString((Object[])((Object[])value));
}
} else {
return value.toString();
}
}
/**
* 获取给定类型的浅层大小,即对象引用大小。
*
* @param type 类型。
* @return 给定类型的浅层大小。
*/
private static int getShallowSize(Class type) {
if (type.isPrimitive()) {
Integer result = (Integer)primitiveSizes.get(type);
return result != null ? result : 0;
} else {
return objectRefSize;
}
}
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe)field.get((Object)null);
objectRefSize = unsafe.arrayIndexScale(Object[].class);
} catch (Exception e) {
throw new RuntimeException(e);
}
// 初始化基本类型大小映射
primitiveSizes = new HashMap(10);
primitiveSizes.put(Byte.TYPE, 1);
primitiveSizes.put(Character.TYPE, 2);
primitiveSizes.put(Integer.TYPE, 4);
primitiveSizes.put(Long.TYPE, 8);
primitiveSizes.put(Float.TYPE, 4);
primitiveSizes.put(Double.TYPE, 8);
primitiveSizes.put(Boolean.TYPE, 1);
}
/**
* 计算给定对象的内存占用。
*
* @param obj 要计算内存占用的对象。
* @return 对象内存占用的详细信息。
* @throws IllegalAccessException 如果访问对象的字段时发生错误。
*/
public ObjectMemoryCalculationInfo calculateMemory(Object obj) throws IllegalAccessException {
ObjectMemoryCalculationInfo result;
try {
result = this.calculateMemory(obj, (Field)null, 0);
} finally {
this.visitedObjects.clear(); // 清理已访问对象缓存
}
return result;
}
}