使用插桩实现对对象大小的计算

参考连接:https://www.iteye.com/blog/yueyemaitian-2033046

1、编写Instrumentation类

创建一个普通类,内含静态方法premain(),这个方法名是java agent内定的方法名,总会在main函数之前执行。

当存在多个premain()重载函数时,参数少的方法会被自动忽略掉。

package com.hpp;

import java.lang.instrument.Instrumentation;

import java.lang.reflect.Array;

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

import java.util.ArrayDeque;

import java.util.Deque;

import java.util.HashSet;

import java.util.Set;

/**

 * 对象占用字节大小工具类

 */

public class SizeOfObject {

    static Instrumentation inst;

    public static void premain(String args, Instrumentation instP) {

        inst = instP;

    }

    /**

     * 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br></br>

     * 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br></br>

     * 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br></br>

     *

     */

    public static long sizeOf(Object obj) {

        return inst.getObjectSize(obj);

    }

    /**

     * 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小

     *

     */

    public static long fullSizeOf(Object objP) throws IllegalAccessException {

        Set<Object> visited = new HashSet<Object>();

        Deque<Object> toBeQueue = new ArrayDeque<Object>();

        toBeQueue.add(objP);

        long size = 0L;

        while (toBeQueue.size() > 0) {

            Object obj = toBeQueue.poll();

            //sizeOf的时候已经计基本类型和引用的长度,包括数组 

            size += skipObject(visited, obj) ? 0L : sizeOf(obj);

            Class<?> tmpObjClass = obj.getClass();

            if (tmpObjClass.isArray()) {

                //[I , [F 基本类型名字长度是2 

                if (tmpObjClass.getName().length() > 2) {

                    for (int i = 0, len = Array.getLength(obj); i < len; i++) {

                        Object tmp = Array.get(obj, i);

                        if (tmp != null) {

                            //非基本类型需要深度遍历其对象 

                            toBeQueue.add(Array.get(obj, i));

                        }

                    }

                }

            } else {

                while (tmpObjClass != null) {

                    Field[] fields = tmpObjClass.getDeclaredFields();

                    for (Field field : fields) {

                        if (Modifier.isStatic(field.getModifiers())//静态不计 

                                || field.getType().isPrimitive()) { //基本类型不重复计 

                            continue;

                        }

                        field.setAccessible(true);

                        Object fieldValue = field.get(obj);

                        if (fieldValue == null) {

                            continue;

                        }

                        toBeQueue.add(fieldValue);

                    }

                    tmpObjClass = tmpObjClass.getSuperclass();

                }

            }

        }

        return size;

    }

    /**

     * String.intern的对象不计;计算过的不计,也避免死循环

     *

     * @param visited

     * @param obj

     * @return

     */

    static boolean skipObject(Set<Object> visited, Object obj) {

        if (obj instanceof String && obj == ((String) obj).intern()) {

            return true;

        }

        return visited.contains(obj);

    }

    public static void main(String[] args) {

        sizeOf(new Object());

    }

}

2、编写达成jar包需要的MANIFEST.MF文件

针对普通的java工程,在和src同级的目录中创建META-INF/MANIFEST.MF文件,并输入如下内容:

Premain-Class: com.hpp.SizeOfObject
Can-Redefine-Classes: true
Can-Retransform-Classes: true

针对maven工程,在resource目录下新建META-INF/MANIFEST.MF文件,配合maven-assembly-plugin插件使用。。。还可以通过maven-jar-plugin插件实现key/value值写入,具体如下

<plugin> 

    <artifactId>maven-jar-plugin</artifactId> 

    <version>2.4</version> 

    <configuration> 

        <finalName>test</finalName> 

        <archive> 

            <manifestEntries> 

                <Premain-class>com.tmall.buy.SizeOfObject</Premain-class> 

                <Can-Retransform-Classes >true</Can-Retransform-Classes

                <Can-Redefine-Classes>false</Can-Redefine-Classes> 

            </manifestEntries> 

            <addMavenDescriptor>false</addMavenDescriptor> 

        </archive> 

    </configuration> 

</plugin> 

3、将Instrumentation类打成相应的jar包

方式1:使用maven_jar_plugin插件

方式2:使用idea打出jar包

4、编写测试类

package com.hpp.test;

import java.io.File;

import static com.hpp.SizeOfObject.fullSizeOf;

import static com.hpp.SizeOfObject.sizeOf;

public class TestObject {

    /**

     * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 = 16

     * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + padding/4 = 24

     */

    static class A {

        int a;

    }

    /**

     * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24

     * -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + 4 = 24

     */

    static class B {

        int a;

        int b;

    }

    /**

     * -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24

     * -XX:-UseCompressedOops: mark/8 + metedata/8 + 8 + 4 + padding/4 = 32

     */

    static class B2 {

        int b2a;

        Integer b2b;

    }

    /**

     * 不考虑对象头:

     * 4 + 4 + 4 * 3 + 3 * sizeOf(B)

     */

    static class C extends A {

        int ba;

        B[] as = new B[3];

        C() {

            for (int i = 0; i < as.length; i++) {

                as[i] = new B();

            }

        }

    }

    static class D extends B {

        int da;

        Integer[] di = new Integer[3];

    }

    /**

     * 会算上A的实例字段

     */

    static class E extends A {

        int ea;

        int eb;

    }

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

        System.out.println(new File("./target/classes").getAbsolutePath());

        System.out.println("sizeOf(new Object())=" + sizeOf(new Object()));

        System.out.println("sizeOf(new A())=" + sizeOf(new A()));

        System.out.println("sizeOf(new B())=" + sizeOf(new B()));

        System.out.println("sizeOf(new B2())=" + sizeOf(new B2()));

        System.out.println("sizeOf(new B[3])=" + sizeOf(new B[3]));

        System.out.println("sizeOf(new C())=" + sizeOf(new C()));

        System.out.println("fullSizeOf(new C())=" + fullSizeOf(new C()));

        System.out.println("sizeOf(new D())=" + sizeOf(new D()));

        System.out.println("fullSizeOf(new D())=" + fullSizeOf(new D()));

        System.out.println("sizeOf(new int[3])=" + sizeOf(new int[3]));

        System.out.println("sizeOf(new Integer(1)=" + sizeOf(new Integer(1)));

        System.out.println("sizeOf(new Integer[0])=" + sizeOf(new Integer[0]));

        System.out.println("sizeOf(new Integer[1])=" + sizeOf(new Integer[1]));

        System.out.println("sizeOf(new Integer[2])=" + sizeOf(new Integer[2]));

        System.out.println("sizeOf(new Integer[3])=" + sizeOf(new Integer[3]));

        System.out.println("sizeOf(new Integer[4])=" + sizeOf(new Integer[4]));

        System.out.println("sizeOf(new A[3])=" + sizeOf(new A[3]));

        System.out.println("sizeOf(new E())=" + sizeOf(new E()));

    }

上述需在测试工程中,引入插桩的实现类

5、测试结果

通过java –jar方式执行,java -javaagent:test.jar -cp test2.jar com.hpp.test.TestObject,其中test.jar和test2.jar,在同一目录中。

通过idea方式执行,引入插桩类后,在启动的vm参数中,增加-javaagent:lib/test.jar配置。

执行结果如下:

在64位机器上,运行结果如下:

sizeOf(new Object())=16

sizeOf(new A())=16

sizeOf(new B())=24

sizeOf(new B2())=24

sizeOf(new B[3])=32

sizeOf(new C())=24

fullSizeOf(new C())=128

sizeOf(new D())=32

fullSizeOf(new D())=64

sizeOf(new int[3])=32

sizeOf(new Integer(1)=16

sizeOf(new Integer[0])=16

sizeOf(new Integer[1])=24

sizeOf(new Integer[2])=24

sizeOf(new Integer[3])=32

sizeOf(new Integer[4])=32

sizeOf(new A[3])=32

sizeOf(new E())=24

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值