1.新建一个agent工具类 , 里面有一个方法sizeOf , 用来返回对象大小。

import java.lang.instrument.Instrumentation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.management.ReflectionException;

public class AgentTools {

    private static Instrumentation inst;

    public static void main(String[] args) throws ClassNotFoundException {
        
    }

    public static void premain(String agentArgs, Instrumentation instP) {
        inst = instP;
    }

    /***
     * 
     * title: 求对象占用堆的大小 
     *
     * @param obj
     * @return
     * @author HadLuo 2020-11-16 9:47:32
     */
    public static long sizeOf(Object obj) {
        return inst.getObjectSize(obj);
    }
}

2.将打包编译上述java文件

javac -d . AgentTools.java

3.新建 META-INF/MANIFEST.mf 清单文件,输入下面内容

Manifest-Version: 1.0
Premain-Class: AgentTools

4.打成jar , 输出为当前目录 , jar名称AgentTools.jar

jar cvfm AgentTools.jar META-INF/MANIFEST.MF .

java stream 获取对象中数值最大 java 获取对象大小_java


5.建立测试类测试

前提:将AgentTools.jar加入path, 或者自定义classloader加载AgentTools.jar ,才能使用反射

public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("AgentTools");
        
        Method method = clazz.getMethod("sizeOf", Object.class);
        long size = (long) method.invoke(null, 1) ;
        System.err.println(size);
    }

启动指定javaagent参数:

-javaagent:F:\eclipse-pro\uckj-git-pro\hadluo-test\src\main\java\AgentTools.jar

运行结果:

java stream 获取对象中数值最大 java 获取对象大小_Java_02


说明 一个 基本类型int 占用16B。

理论值:Mark Word(8B)+ kclass(4B) + i(4B) = 16B

对象头(Header)
1. Mark Word: 虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如 hashCode 、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。这部分数据的长度在 32 位和 64 位的虚拟机(未开启指针压缩)中分别为 4B 和 8B ,官方称之为 ”Mark Word”。

2. kclass: 对象的另一部分是类型指针(kclass),即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例。另外如果对象是一个 Java 数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通 Java 对象的元数据信息确定 Java 对象的大小,但是从数组的元数据中却无法确定数组的大小。同样,这部分数据的长度在 32 位和 64 的虚拟机(未开启指针压缩)中分别为 4B 和 8B。
指针压缩

3. 实例数据

java stream 获取对象中数值最大 java 获取对象大小_Java_03


4. 对齐填充(Padding)

由于虚拟机内存管理体系要求 Java 对象内存起始地址必须为 8 的整数倍,换句话说,Java 对象大小必须为 8 的整数倍,当对象头+实例数据大小不为 8 的整数倍时,将会使用Padding机制进行填充,譬如, 64 位虚拟机上 new Object() 实际大小为:

Mark Word(8B)+ kclass(4B)[开启指针压缩] = 12B

但由于Padding机制,实际占用空间为:

Mark Word(8B)+ kclass(4B)[开启指针压缩]+Padding(4B) = 16B

5. 数组的大小
Java 中数组也是一种对象,数组的大小与普通 Java 对象相比多了数组长度的信息(4B),即一个数组对象大小为
Mark Word(8B)+ kclass(4B)[开启指针压缩] + 数组长度(4B) = 16B