Java对象在内存中存储布局及大小

本文详细介绍了Java对象在内存中的存储布局,包括MarkWord、ClassPointer、实例数据和对齐填充等部分,并讨论了-XX:+UseCompressedClassPointers和-XX:+UseCompressedOops选项对对象大小的影响。通过示例展示了如何计算不同配置下对象的大小,以及如何利用Java Agent计算对象的实际内存占用。
摘要由CSDN通过智能技术生成

对象在内存中的存储布局

关于该问题需要拆分为两种情况分析:

  1. 普通对象:
  • 对象头MarkWord: 8个字节

  • Class pointer: 属于哪个Class,-XX:+UseCompressedClassPointers 为4字节,否则为8字节。

  • 实例数据InstantData:

    • 引用类型: -XX:+UseCompressedOops 开启则占4字节,否则占8字节
  • 对齐填充Padding:保证对象的大小为8的整数倍。
  1. 数组对象:
  • MarkWord: 8个字节
  • ClassPointer: -XX:+UseCompressedClassPointers 为4字节,否则为8字节。
  • 数组长度: 4个字节
  • 数组数据
  • 对齐填充:保证对象的大小为8位的整数倍。

整个对象的结构大致如下图:

 object model

对象的大小

对象的实际大小其实和虚拟机的配置有关联,我们可以通过java -XX:+PrintCommandLineFlags -version 输出当前JVM的配置信息:

java -XX:+PrintCommandLineFlags -version
        
以下是笔者安装的JVM, 输出如下:
     -XX:InitialHeapSize=268435456
     -XX:MaxHeapSize=4294967296
     -XX:+PrintCommandLineFlags
     -XX:+UseCompressedClassPointers        // class pointer 压缩
     -XX:+UseCompressedOops                 // ordinary object pointer 普通对象的指针压缩
     -XX:+UseParallelGC 
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

在上面的输出信息有两个配置内容关系到一个对象的实际大小:

  • -XX:+UseCompressedClassPointers: 表示开启 class pointer 指针压缩,class pointer 原始占用8个字节,开启压缩指针后占用4个字节,可以通过-UseCompressedClassPointers关闭指针压缩。
  • -XX:+UseCompressedOops: 表示开启普通对象的指针压缩,原始对象引用指针占用8个字节,开启压缩指针后占用4个字节,可以通过-UseCompressedClassPointers关闭指针压缩。

知道上述两个因素之后,就可以大致计算出一个对象的具体大小:

    /**
     * -XX:+UserCompressedClassPointers 为24
     * -XX:-UserCompressedClassPointers 为32
     * -XX:-UserCompressedClassPointers -XX:-UseCompressedOops 为 40
     */
    public static class P {
        // mark word 8 字节
        // class pointer 压缩为4字节,不压缩为8字节

        // int 类型为 4 个字节
        int a;
        int b;
        int c;

        // String 为引用类型,开启oops压缩占4个字节,正常占8个字节
        String s = "s";

        // padding 对其填充为 8 字节倍数
    }

小技巧利用 Agent 计算对象的大小

在ClassLoader中提到,一个Class文件通过ClassLoader load 到内存中的时候,我们可以通过在这个过程中增加一个 agent,通过 agent 可以获取到对象的大小,具体步骤如下:

  1. 新建一个 Module,添加ObjectAgent.java
    public class ObjectAgent {
    
        static Instrumentation inst;
    		
      	// 该方法签名是固定的,就和main方法类似
        public static void premain(String args, Instrumentation _inst) {
            inst = _inst;
        }
    
      	// 计算对象的大小
        public static long sizeof(Object o) {
            return inst.getObjectSize(o);
        }
    }
  1. 在 src 目录下添加 META-INF/MANIFEST.MF文件,内容为(注:最后必须另起一个空行):
	Manifest-Version: 1.0
	Premain-Class: object_agent.ObjectAgent

  1. 将该module打成jar包,添加到需要使用的module的library中

  2. 添加启动VM参数:-javaagent:${ObjectAgent.jar的路径}

  3. 调用ObjectAgent.sizeof(o);

    public class ObjectSize {
    
        public static void main(String[] args) {
            Object o = new Object();
            System.out.println("new Object() 对象大小为:" + ObjectAgent.sizeof(o));
    
            // -XX:+UserCompressedClassPointers 为16
            // -XX:-UserCompressedClassPointers 为24
            T t = new T();
            System.out.println("new T() 对象大小为:" + ObjectAgent.sizeof(t));
    
            // -XX:+UserCompressedClassPointers 为24
            // -XX:-UserCompressedClassPointers 为32
            // -XX:-UserCompressedClassPointers -XX:-UseCompressedOops 为 40
            P p = new P();
            System.out.println("new P() 对象大小为:" + ObjectAgent.sizeof(p));
        }
    
        public static class T {
            // mark word 8 字节
            // class pointer 压缩为4字节,不压缩为8字节
    
            // int 类型为 4 个字节
            int a;
    
            // padding 对其填充为 8 字节倍数
        }
    
        /**
         * -XX:+UserCompressedClassPointers 为24
         * -XX:-UserCompressedClassPointers 为32
         * -XX:-UserCompressedClassPointers -XX:-UseCompressedOops 为 40
         */
        public static class P {
            // mark word 8 字节
            // class pointer 压缩为4字节,不压缩为8字节
    
            // int 类型为 4 个字节
            int a;
            int b;
            int c;
    
            // String 为引用类型,开启oops压缩占4个字节,正常占8个字节
            String s = "s";
    
            // padding 对其填充为 8 字节倍数
        }
    

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值