• 复杂Java对象所占内存的大小


    我们在Java单个对象内存布局中讲解了单个简单的Java对象所占内存的大小的计算。那么这篇文章主要是讲解复杂Java对象所占内存大小的计算,我们把继承、复合的对象称为复杂对象

    继承对象

    class Parent {
        protected int x; // 4字节
        protected int y; // 4字节
    
        protected boolean flag; // 1字节
    }
    
    class Child extends Parent {
        private int z; // 4字节
    }
    
    public class ExtendsObjectSizer {
        public static void main(String[] args) {
            System.out.println("继承对象的大小为:" + ObjectSizeFetcher.sizeOf(new Child()) + "字节");
        }
    }
    

      

    然后重新打包,执行如下的命令:

    ## 没有开启指针压缩功能
    java -XX:-UseCompressedOops -javaagent:ObjectSizeFetcherAgent-1.0-SNAPSHOT.jar com.twq.ExtendsObjectSizer

    得到的结果如下:

     可以看出new Child()的内存大小为40字节,那么这个40字节是怎么来的呢?我们看下图: 

     

    40字节 = 16 + (4 + 4 + 1 + 7) + 4 + 7

    ## 开启指针压缩功能
    java -XX:+UseCompressedOops -javaagent:ObjectSizeFetcherAgent-1.0-SNAPSHOT.jar com.twq.ExtendsObjectSizer
    

      得到的结果如下:

     可以看出new Child()的内存大小为32字节,那么这个32字节是怎么来的呢?我们看下图: 

    32字节 = 12 + 4 + 4 + 1 + 7 + 4

    总结:继承对象也只有一个对象头,继承对象所占内存的大小由4部分组成:

    1. 对象头
    2. 父亲属性所占内存大小,需要对齐补充至8的倍数
    3. 子类属性所占内存大小
    4. 对齐补充

    复合对象

    我们接下来看下复合对象所占内存大小,先看下如下代码:

    class Employee {
        private int age; // 4字节
        private double high; // 8字节
    }
    
    class Dept {
        private int num; // 4字节
    
        private Employee[] employees = new Employee[3];
    
        public Dept() {
            for (int i = 0; i < employees.length; i++) {
                employees[i] = new Employee();
            }
        }
    }
    
    public class CompositeObjectSizer {
        public static void main(String[] args) throws IllegalAccessException {
            System.out.println("复合对象内存的大小为:" + ObjectSizeFetcher.sizeOf(new Dept()) + "字节");
            System.out.println("复合对象内存的总大小为:" + ObjectSizeFetcher.fullSizeOf(new Dept()) + "字节");
        }
    }
    

      然后重新打包,执行如下的命令:

    得到的结果如下:

    可以看出复合对象内存的大小为32字节 = 对象头16字节 + num属性4字节 + employees引用类型的大小8字节 + 对齐补充4字节

    这个32字节也只是直接计算当前Dept对象占用空间大小,这个大小并没有包含数组employees中所有的Employee的内存大小,那么这个复合对象的总大小为176字节,这个大小包括了数组employees中所有的Employee的内存大小,也就是递归计算当前对象占用空间总大小。

    递归计算复合对象占用的内存的时候需要注意的是:对齐填充是以每个对象为单位进行的,看下面这个图就很容易明白: 

    根据上图,我们手动来计算一下new Dept()的总内存大小,分为3部分:

    • Dept实例对象本身的大小为:16 + 4 + 8 + 4 = 32字节
    • 数组对象employees的大小为:24 + 8 * 3 = 48字节
    • 3个Employee对象的大小为:3 * (16 + 4 + 8 + 4) = 96字节

    所以内存总大小为:32 + 48 + 96 = 176字节

    我们再来看下开启了指针压缩功能的复合对象内存的大小情况,我们执行如下的命令:

    ## 开启指针压缩功能
    java -XX:+UseCompressedOops -javaagent:ObjectSizeFetcherAgent-1.0-SNAPSHOT.jar com.twq.CompositeObjectSizer
    

      

    得到的结果如下:

     

     可以看出复合对象内存的大小为24字节 = 对象头12字节 + num属性4字节 + employees引用类型的大小4字节 + 对齐补充4字节

    在开启指针压缩功能的情况下,Dept复合对象的总大小为128字节,如下图: 

    根据上图,我们手动来计算一下new Dept()的总内存大小,分为3部分:

    • Dept实例对象本身的大小为:12 + 4 + 4 + 4 = 24字节
    • 数组对象employees的大小为:16 + 4 * 3 + 4 = 32字节
    • 3个Employee对象的大小为:3 * (12 + 4 + 8) = 72字节

    所以内存总大小为:24 + 32 + 72 = 126字节

     

    ArrayList

    以下是ArrayList的部分源码:

    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
        protected transient int modCount = 0;
    }
    
    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
        private static final long serialVersionUID = 8683452581122892189L;
    
        /**
         * Default initial capacity.
         */
        private static final int DEFAULT_CAPACITY = 10;
    
        /**
         * Shared empty array instance used for empty instances.
         */
        private static final Object[] EMPTY_ELEMENTDATA = {};
    
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
        transient Object[] elementData; // non-private to simplify nested class access
    
        /**
         * The size of the ArrayList (the number of elements it contains).
         */
        private int size;
    }
    

      

    static变量属于类,不属于实例,存放在全局数据段。普通变量才纳入Java对象占用空间的计算,一个用于存放数组元素的Object[], 一个int类型的size,还有一个是父类中int类型的modCount。因此:

    • 在64位操作系统,且未开启指针压缩功能的前提下,new ArrayList<Integer>()的内存大小应为:对象头16字节 + 父类属性modCount大小4字节 + 对齐补充4字节 + 子类Object[]引用类型的8字节 + 子类int类型的属性大小4字节 + 对齐补充4字节 = 40字节
    • 在64位操作系统,且开启了指针压缩功能的前提下,new ArrayList<Integer>()的内存大小应为:对象头12字节 + 父类属性modCount大小4字节 + 对齐补充4字节 + 子类Object[]引用类型的4字节 + 子类int类型的属性大小4字节 + 对齐补充4字节 = 32字节

     

  • 相关阅读:
    Spark2.3(三十六):根据appName验证某个app是否在运行
    Spark2.3(三十五)Spark Structured Streaming源代码剖析(从CSDN和Github中看到别人分析的源代码的文章值得收藏)
    Spark:实现行转列
    Spark2.3(三十四):Spark Structured Streaming之withWaterMark和windows窗口是否可以实现最近一小时统计
    Spark2.2(三十三):Spark Streaming和Spark Structured Streaming更新broadcast总结(一)
    Centos7:Failed to start LSB: Bring up/down networking
    CDH:cdh5环境搭建
    CDH:cdh5环境mkdir: Permission denied: user=root, access=WRITE, inode="/user":hdfs:hadoop:drwxr-xr-x
    Spark2.2+ES6.4.2(三十二):ES API之index的create/update/delete/open/close(创建index时设置setting,并创建index后根据avro模板动态设置index的mapping)
    Spark2.2+ES6.4.2(三十一):Spark下生成测试数据,并在Spark环境下使用BulkProcessor将测试数据入库到ES
  • 原文地址:https://www.cnblogs.com/tesla-turing/p/11487754.html
Copyright © 2020-2023  润新知