2

java 一个对象内存有多大

为什么想知道这个,自以为很重要,其实 just for fun =。=

测试相关说明

  • jdk: java 官网上下载的,HotSpot 虚拟机

    java version "1.8.0_121"
    Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
    Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
  • 使用的方法: Instrumentation 参考的是 一个对象占用多少字节?
  • 确认编译时是 32 位还是 64 位,对测试结果有影响。x86 为 32 位, amd64 为 64 位,其余我不知道

    System.out.println(System.getProperty("os.arch"));

准备工作

  • SizeOfObject.java 直接从上面链接上拷贝
  • 编译这个类,得到 .class 文件
  • 在包名路径下执行一下命令进行打包(注意修改相应的包名):

    jar cvfm SizeOfObject.jar manifest.mf org/seal_de/SizeOfObject.class
    • 其中 manifest.mf 清单信息为: PreMain-Class: org.seal_de.SizeOfObject
    • PS: 如果在打包好的 jar 包中,META-INF/MANIFEST.MF 没有 PreMain-Class 属性,添加上去即可
  • 在运行测试程序的时候,添加 vm 参数

    -javaagent:{jar包路径}\SizeOfObject.jar

测试用例

  • 测试 int, Object, 引用的大小。其余类型测试都类似
public class MemoryTest {
    /**
     * -javaagent:{jar包路径}\SizeOfObject.jar -XX:+UseCompressedOops
     * 使用指针压缩,在一定情况下64位HotSpot jvm默认指针压缩
     *
     *Output:
     *amd64
     *Object:                              16
     *    
     *include one int:                     16
     *include two int:                     24
     *include three int:                   24
     *
     *include one object:                  16
     *include one object:                  24
     */
    static void test1() {
        System.out.println(System.getProperty("os.arch"));
        System.out.printf("%-30s%9d\n", "Object:", SizeOfObject.sizeOf(new Object()));
        System.out.println();
        System.out.printf("%-30s%9d\n", "include one int:", SizeOfObject.sizeOf(new IntegerTestOne()));
        System.out.printf("%-30s%9d\n", "include two int:", SizeOfObject.sizeOf(new IntegerTestTwo()));
        System.out.printf("%-30s%9d\n", "include three int:", SizeOfObject.sizeOf(new IntegerTestThree()));
        System.out.println();
        System.out.printf("%-30s%9d\n", "include one object:", SizeOfObject.sizeOf(new ReferenceTestOne()));
        System.out.printf("%-30s%9d\n", "include one object:", SizeOfObject.sizeOf(new ReferenceTestTwo()));
    }

    /**
     * -javaagent:{jar包路径}\SizeOfObject.jar -XX:-UseCompressedOops
     * 不使用指针压缩
     *
     *Output:
     *amd64
     *Object:                              16
     *
     *include one int:                     24
     *include two int:                     24
     *include three int:                   32
     *
     *include one object:                  24
     *include one object:                  32
     */
    static void test2() {
        System.out.println(System.getProperty("os.arch"));
        System.out.printf("%-30s%9d\n", "Object:", SizeOfObject.sizeOf(new Object()));
        System.out.println();
        System.out.printf("%-30s%9d\n", "include one int:", SizeOfObject.sizeOf(new IntegerTestOne()));
        System.out.printf("%-30s%9d\n", "include two int:", SizeOfObject.sizeOf(new IntegerTestTwo()));
        System.out.printf("%-30s%9d\n", "include three int:", SizeOfObject.sizeOf(new IntegerTestThree()));
        System.out.println();
        System.out.printf("%-30s%9d\n", "include one object:", SizeOfObject.sizeOf(new ReferenceTestOne()));
        System.out.printf("%-30s%9d\n", "include one object:", SizeOfObject.sizeOf(new ReferenceTestTwo()));
    }

    public static void main(String[] args) {
        test2();
    }

    static class IntegerTestOne {
        private int i1 = 1;
    }

    static class IntegerTestTwo {
        private int i1 = 1;
        private int i2 = 1;
    }

    static class IntegerTestThree {
        private int i1 = 1;
        private int i2 = 1;
        private int i3 = 1;
    }

    static class ReferenceTestOne {
        private Object o1 = new Object();
    }

    static class ReferenceTestTwo {
        private Object o1 = new Object();
        private Object o2 = new Object();
    }
}

一些概念

  • 对象内存 = 对象头 + 类型指针 + 对齐填充
  • 对象头不参与指针压缩,并且 32 位时为 4 个字节,64 位时为 8 个字节
  • 类型指针参与指针压缩,并且 32 位时为 4 个字节,64 位时为 8 个字节;指针压缩时 64 位为 4 个字节
  • 对齐填充,由于 jvm 设计内存要为 8 字节的整数倍,所以不足的需要填充。如 对象头和类型指针一共 12 字节,填充后为 16 字节,填充了 4 个字节

测试结果验证上面的假设

  • 其中 (8 + 8) 为对象头和类型指针的字节数
64 位 -XX:-UseCompressedOops -XX:+UseCompressedOops
Object 8 + 8 = 16 (对象头+类型指针) 8 + 4 + 4 = 16(对象头+压缩的类型指针+对齐填充)
包含一个int (8 + 8) + 4 + 4 = 24 (8 + 4) + 4 = 16
包含两个int (8 + 8) + 4*2 = 24 (8 + 4) + 4*2 + 4 = 24
包含三个int (8 + 8) + 4*3 + 4 = 32 (8 + 4) + 4*3 = 24
不压缩引用占 8 个字节,压缩占 4 个字节
包含一个引用 (8 + 8) + 8 = 24 (8 + 4) + 4 = 16
包含两个引用 (8 + 8) + 8*2 = 32 (8 + 4) + 4*2 + 4 = 24

静态数据不在对象内存里面

public class MemoryStaticTest {
    /**
     * -javaagent:{jar包路径}\SizeOfObject.jar
     * 使用指针压缩,在一定情况下64位HotSpot jvm默认指针压缩
     *
     * Output:
     * amd64    +UseCompressedOops
     * StaticTest:                          16
     * Integer:                             16
     * StaticReferenceTest:                 16
     *
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(System.getProperty("os.arch") + "    +UseCompressedOops");
        System.out.printf("%-30s%9d\n", "StaticTest:", SizeOfObject.sizeOf(new StaticTest()));
        System.out.printf("%-30s%9d\n", "Integer:", SizeOfObject.sizeOf(new Integer(1)));
        System.out.printf("%-30s%9d\n", "StaticReferenceTest:", SizeOfObject.sizeOf(new StaticReferenceTest()));
    }

    static class StaticTest {
        private static int i1 = 1;
        private static int i2 = 1;
        private static int i3 = 1;
        private static int i4 = 1;
        private static int i5 = 1;
        private static int i6 = 1;
    }

    static class StaticReferenceTest {
        private static Object o1 = new Object();
        private static Object o2 = new Object();
        private static Object o3 = new Object();
        private static Object o4 = new Object();
        private static Object o5 = new Object();

    }
}

为什么是这个样子呢

  • 对象头在 64 位的时候,为什么是 8 个字节;32 位的时候,为什么是 4 个字节
  • 一个引用在 64 位的时候,为什么是 8 个字节;32 位的时候,为什么是 4 个字节

    • 这个跟 C++ 指针有关了,跟寄存器有关了
  • 指针压缩又是怎么个回事

这一切都可以在 jvm 的实现中找到答案,也就是看 C++ 代码。只是我还没到这个阶段=。=

参考链接


seal_de
46 声望3 粉丝