计算对象在JVM中存储的大小
文章目录
在一些监控中发现序列化后的对象实际没有特别大,但在JVM中占用的内存却很大,甚至导致了OOM(Out of Memory)。
写了一个简单的测试来计算对象在JVM中存储的大小。
源码
import com.au92.common.util.json.JsonUtils;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
/**
* 计算对象内存大小的工具类
*
* @author p_x_c
*/
public class MemoryMeasurer {
// 默认类型大小(单位:字节)
private static final Map<Class<?>, Long> DEFAULT_SIZES = new HashMap<>();
static {
DEFAULT_SIZES.put(byte.class, 1L);
DEFAULT_SIZES.put(short.class, 2L);
DEFAULT_SIZES.put(int.class, 4L);
DEFAULT_SIZES.put(long.class, 8L);
DEFAULT_SIZES.put(float.class, 4L);
DEFAULT_SIZES.put(double.class, 8L);
DEFAULT_SIZES.put(char.class, 2L);
DEFAULT_SIZES.put(boolean.class, 1L);
DEFAULT_SIZES.put(Byte.class, 1L);
DEFAULT_SIZES.put(Short.class, 2L);
DEFAULT_SIZES.put(Integer.class, 4L);
DEFAULT_SIZES.put(Long.class, 8L);
DEFAULT_SIZES.put(Float.class, 4L);
DEFAULT_SIZES.put(Double.class, 8L);
DEFAULT_SIZES.put(Character.class, 2L);
DEFAULT_SIZES.put(Boolean.class, 1L);
DEFAULT_SIZES.put(String.class, 40L); // 默认初始大小,真实内容另计
}
/**
* 估算对象的内存大小(递归)
*/
public static long sizeOf(Object obj) {
return sizeOf(obj, new IdentityHashMap<>());
}
private static long sizeOf(Object obj, IdentityHashMap<Object, Boolean> visited) {
if (obj == null || visited.containsKey(obj)) {
return 0L;
}
visited.put(obj, true);
Class<?> clazz = obj.getClass();
// 基本类型包装类或常用类型(直接估算)
if (DEFAULT_SIZES.containsKey(clazz)) {
return DEFAULT_SIZES.get(clazz);
}
// 字符串特殊处理
if (obj instanceof String str) {
return 40L + str.length() * 2L; // header + char[] UTF-16
}
// 数组
if (clazz.isArray()) {
int len = Array.getLength(obj);
long size = 16L; // array object header
for (int i = 0; i < len; i++) {
size += sizeOf(Array.get(obj, i), visited);
}
return size;
}
// 集合
if (obj instanceof Collection<?> coll) {
long size = 24L; // collection object header
for (Object item : coll) {
size += sizeOf(item, visited);
}
return size;
}
// Map
if (obj instanceof Map<?, ?> map) {
long size = 32L; // map object header
for (Map.Entry<?, ?> entry : map.entrySet()) {
size += sizeOf(entry.getKey(), visited);
size += sizeOf(entry.getValue(), visited);
}
return size;
}
// 普通对象:递归所有字段
long totalSize = 16L; // object header
for (Field field : getAllFields(clazz)) {
field.setAccessible(true);
try {
Object fieldValue = field.get(obj);
totalSize += sizeOf(fieldValue, visited);
} catch (IllegalAccessException ignored) {
}
}
return totalSize;
}
/**
* 获取所有字段(包括父类)
*/
private static List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
while (clazz != null && clazz != Object.class) {
Collections.addAll(fields, clazz.getDeclaredFields());
clazz = clazz.getSuperclass();
}
return fields;
}
}
测试代码
public static class Test {
public Test(int id) {
}
}
public static void main(String[] args) {
Map<String, Object> data = new HashMap<>();
data.put("id", 123);
data.put("name", "测试用户");
data.put("active", true);
data.put("tags", Arrays.asList("a", "b", "c", "d", "e"));
data.put("numbers", Arrays.asList(1, 2, 3, 4));
data.put("testObject", new Test(3));
long size = MemoryMeasurer.sizeOf(data);
System.out.println("估算JVM内存大小:" + size + " bytes");
String json = JsonUtils.toJSONString(data);
byte[] bytesx = json.getBytes(StandardCharsets.UTF_8);
System.out.println("实际传输数据 字节大小: " + bytesx.length + " bytes");
}
文章作者 pengxiaochao
上次更新 2025-08-01
许可协议 不允许任何形式转载。