Java 获取 List 对象内存大小

在 Java 开发中,我们经常需要统计和管理对象的内存使用情况。对于 List 对象来说,如何获取其占用的内存大小是一个常见的需求。本文将介绍几种在 Java 中获取 List 对象内存大小的方法,并提供相应的代码示例。

方法一:使用 Instrumentation

Java 提供了一个 Instrumentation 接口,可用于测量对象的大小。通过该接口,我们可以获取 List 对象占用的内存大小。

import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.List;

public class MemoryUtil {

    private static Instrumentation instrumentation;

    public static void premain(String args, Instrumentation inst) {
        instrumentation = inst;
    }

    public static long getObjectSize(Object obj) {
        if (instrumentation != null) {
            return instrumentation.getObjectSize(obj);
        }
        throw new IllegalStateException("Instrumentation is not initialized");
    }

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        long size = getObjectSize(list);
        System.out.println("List object size: " + size + " bytes");
    }
}

在上述代码中,我们通过 Instrumentation.getObjectSize() 方法获取 List 对象的大小,并输出到控制台。

要使用 Instrumentation,需要在 JVM 启动时添加 -javaagent 参数,并指定一个代理类。在代理类中,我们可以通过实现 premain 方法来初始化 Instrumentation 对象。在 getObjectSize 方法中,我们通过检查 Instrumentation 对象是否已初始化来确保获取对象大小的正确性。

方法二:使用 Object 测量方法

除了使用 Instrumentation 接口外,我们还可以通过创建一个临时的 Object 对象,然后计算其大小差值来估算 List 对象的大小。这种方法不需要使用 Instrumentation 接口,但可能不够准确。

import java.util.ArrayList;
import java.util.List;

public class MemoryUtil {

    public static long getObjectSize(Object obj) {
        long baseSize = getBaseSize();
        long referenceSize = getReferenceSize();

        long objectSize = baseSize + referenceSize * ((List<?>) obj).size();
        return align(objectSize);
    }

    private static long getBaseSize() {
        return align(12); // 对象头部分配 12 个字节
    }

    private static long getReferenceSize() {
        return align(4); // 指针大小为 4 个字节
    }

    private static long align(long size) {
        return (size + 7) / 8 * 8; // 对齐到 8 的倍数
    }

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        long size = getObjectSize(list);
        System.out.println("List object size: " + size + " bytes");
    }
}

在上述代码中,我们估算 List 对象的大小,首先需要计算一个对象的基本大小(baseSize)和一个引用的大小(referenceSize)。基本大小是对象头部分配的字节数,引用大小是指针的大小。

然后,我们将基本大小和引用大小乘以 List 对象的元素个数,得到总的对象大小。最后,我们对对象大小进行对齐操作,以满足内存对齐的要求。

方法三:使用第三方库

除了上述两种方法,还可以使用一些第三方库来获取 List 对象的内存大小。例如,Apache Commons Lang3 库提供了 SizeOf 类,可以用于测量对象的大小。

import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class MemoryUtil {

    public static long getObjectSize(Object obj) throws Exception {
        Method sizeMethod = MethodUtils.getAccessibleMethod(obj.getClass(), "size");
        int size = (int) sizeMethod.invoke(obj);

        Field elementDataField = FieldUtils.getDeclaredField(obj.getClass(), "elementData", true);
        Object[] elementData = (Object[]) elementDataField.get(obj);

        long objectSize = 12 + 4 + size * 4 + elementData.length * 4;
        return align(objectSize);
    }

    private static long align(long size) {
        return (size + 7) / 8 *