java对象占用多大内存

经典面试题之new一个java对象占用多大的内存

在hotspot虚拟机中,对象的存储可以分为,对象头 header,示例数据 Instance Data,和补齐填充 padding三个区域

首先我们可以查看header的结构
header包含三个部分,markword、classpointer、arraylength,

64位操作系统的jvm的markword存储结构如下所示,32位操作系统的不做叙述,本文重点不是介绍markword,旨在介绍它的长度,关旭markword,有时间再整理稳扎

java对象占用多大内存插图

可以看到markword在64位操作系统上的占用内存为64bit
classpointer 存储对象的类型指针,jvm通过这个指针可以确定对象是那个类的实例,长度为jvm的一个字大小,64位操作系统下的jvm占用64bit空间,32位操作系统下占用32bit的内存空间。
arrylength 只在对象是数组的情况下载会占用内存,存储改数组对象的长度。同样也是随着操作系统的变化而变化,64位的操作系统下占用64bit空间,32位操作系统下占用32bit的存储空间。
为了避免指针占用大量空间浪费内存,java提供了指针压缩个功能,可以开启指针压缩,java8默认已经是开启的。开启指针压缩后,classpointer和arraylength都将被压缩50%,64位操作系统下,各自只占用32bit的存储空间。

根据上面的结果我们可以计算出在开启java指针压缩的情况下,64位操作系统的一个非数组对象的header有64+32 = 96bit,如果是一个数组对象那么久应该有64+32+32 = 128bit。

通过命令可以查看当前jvm是否启用指针压缩

 root~$ java -XX:+PrintCommandLineFlags -version 
-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)

UseCompressedClassPointers 表示压缩指针,如果当前运行在64位的操作系统上,那么指针长度为64bit也就是8个byte。如果开启指针压缩,classpointer被压缩为4byte, UseCompressedOops表示压缩普通对象指针,占用4byte

Instance Data 部分占用多大内存这个需要取决于new的对象里面的存储属性一个指针引用占64bit,开启普通对象指针压缩的话占用32bit。一个int 占用32bit等

padding jvm的一个特性8字节对其。大致对象占用的内存必须是8字节对其的,比如有个对象占用了。12个字节,那么就会自动不全4个字节,具体原因这里不做赘述

了解了上面的一些基本特性,我们可以从代码去验证下上面的数据

new一个Object对象占多大内存

根据上面的推论,我们可以得到如果我们仅仅只是new 一个Object对象的话,那么只有对象头的96bit占用空间,他也没有其他属性占用空间,
在计算补全填充32bit 总共占用128bit=16个byte,也就是new一个object对象一共需要炸用16个字节,128个字符,当前这个前提是64位操作系统,开启了指针压缩。
借助jol我们可以数据内存占用情况

        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.10</version>
        </dependency>

    public static void main(String[] args) {
        System.out.println(ClassLayout.parseInstance(new Object()).toPrintable());
    }

果不其然,我们发现object独享占用了12byte,也就是96bit,遵循jvm的8字节对齐原则,补齐了4个字节的存储空间一个占用了16 bytes的内存

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

new一个带属性对象占用多大内存

上面说了new一个object 对象这个对象里面没有属性,那么如果我们new了一个有自定义的有属性的额对象,占用多大内存呢。

    public static void main(String[] args) {
        System.out.println(ClassLayout.parseInstance(new Person()).toPrintable());
    }

    static class Person{
        String name;
        private int age;

        public Person() {
        }

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }

首先我们header占用96bit也就是12bytes 在实例数据Instance Data部分,我们可以看到有两个一个是age,一个是name,int类型的age占用4byte内存,String类型的name占用了4byte的内存(原因64位操作系统开启了普通对象指针压缩,空间压缩为原来的50%)所以实际占用了12+4+4 = 20bytes内存,
遵循jvm的8byte补齐原则,padding了4bytes的内存。一个占用了24bytes

输出结果如下,一共占用24byte,其中头对象占了12个字节,补全占了4个字节,

 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                    (object header)                           92 c3 00 f8 (10010010 11000011 00000000 11111000) (-134167662)
     12     4                int Person.age                                0
     16     4   java.lang.String Person.name                               null
     20     4                    (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

数组对象占用多大内存

上面的例子没有体现arraylength的长度特性,下面代码中我们给出了已个new了数字的例子

    public static void main(String[] args) {
        System.out.println(ClassLayout.parseInstance(new Object[5]).toPrintable());
    }

这里我们可以发现header占用了16bytes的内存。因为多了一个4bytes的arraylength,存储数组的长度,然后接着示例数据占用了5*4 = 20bytes的内存空间。
一共占用了36bytes的内存,一句jvm的8byte补齐原则,本次需要补齐4bytes。一共占用40bytes。实际输出结果也是Instance size: 40 bytes

[Ljava.lang.Object; object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                    (object header)                           4c 23 00 f8 (01001100 00100011 00000000 11111000) (-134208692)
     12     4                    (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)
     16    20   java.lang.Object Object;.<elements>                        N/A
     36     4                    (loss due to the next object alignment)
Instance size: 40 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

409 对 “java对象占用多大内存”的想法;

发表评论