jvm之对象大小分析

本文详细分析了Java对象在内存中的布局,包括对象头、对象体、内存对齐、包装类型与原始类型的区别,以及数组和String的内存占用情况。通过实例展示了如何计算对象的大小,并指出在编程中应尽量使用基本数据类型和避免多维数组以优化内存使用。
摘要由CSDN通过智能技术生成

写在前面

本文看下计算对象大小相关内容。

1:基础内容

1.1:对象的结构

一个对象由对象头和对象体组成,其中对象头包含如下内容:

标记字(mark word):存放GC年龄信息,对象锁信息等,占用8个字节
class指针:指向方法区中的class文件信息,占用4个字节(指针压缩)
数组长度信息(array length):数组特有,记录数组的长度,int表示,所以是8个字节,该项非数组可忽略

对象体存储的是具体的对象内容以及内部padding:

对象内容:具体的属性信息
内部padding:如果不是8字节的整数倍,则填充为8字节的整数倍,因为CPU是以8byte为单位来获取数据的

当对象头和对象体的总大小不是8byte的整数倍时需要通过外部aligment来填充到整数倍,整体结构如下:

在这里插入图片描述

因此就算是一个空对象其大小也至少是标记字8字节+class指针4字节+对象内容0字节+外部alignment4字节=16字节。如下定义一个类:

public class EmptyObject {
}

然后创建100个对象实例,如下:

public class Main {

    public static void main(String[] args) throws InterruptedException {
        List<EmptyObject> oneHundredList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            oneHundredList.add(new EmptyObject());
        }
        Thread.sleep(Integer.MAX_VALUE);
    }
}

如下查看:

C:\Users\Administrator>jps -l
7984 org.jetbrains.jps.cmdline.Launcher
12692
20196 sun.tools.jps.Jps
3512 dongshi.daddy.objectsize.Main
14892
19580 org.jetbrains.idea.maven.server.RemoteMavenServer36
C:\Users\Administrator>jmap -histo 3512 > d:\\test\\emptyobj100.txt

在这里插入图片描述

可以看到每个是16字节,当我们定义一个int属性,大小是多少呢?依然是16字节,因为有了4字节的int就不要4字节的外部alignment了,如下:

public class EmptyObject {
    int num;
}

重复执行创建100个对象实例的程序:

C:\Users\Administrator>jps -l
11780 sun.tools.jps.Jps
12692
12568 dongshi.daddy.objectsize.Main
14892
19356 org.jetbrains.jps.cmdline.Launcher
19580 org.jetbrains.idea.maven.server.RemoteMavenServer36

C:\Users\Administrator>jmap -histo 12568 > d:\\test\\withint.txt

在这里插入图片描述

那如果我们再添加一个short成员变量,此时一个EmptyObject占用多大内存呢?答案是24字节,计算如下:

对象头标记字:8字节
对象头class指针:4字节

对象内容int:4字节
对象内容short:2字节
内部padding:2字节

此时总共20字节,不是8整倍数,所以需要外部alignment4字节

所以总共是24字节

修改如下:

public class EmptyObject {
    int num;
    short shortNum;
}

重新生成100个对象实例后查看:

C:\Users\Administrator>jmap -histo 3952 | findstr ":EmptyObject"

C:\Users\Administrator>jmap -histo 3952 | findstr "EmptyObject"
  28:           100           2400  dongshi.daddy.objectsize.EmptyObject

可以看到确实是24字节。

2:包装类型和原生类型

先说结论,包装数据类型要比原生类型占用更多的内存,因为多了对象头和填充的内存占用,分别来看下Integer和Long。

2.1:Integer

对于int,其占用4个字节,但是如果是其包装类型Integer,则要占用(对象头标记字8字节+对象头class指针4字节+对象内容int本身4字节=16字节),如下也可以验证:

C:\Users\Administrator>jmap -histo 3952 | findstr "java.lang.Integer"
  19:           258           4128  java.lang.Integer

总大小4128除以对象个数258等于16字节。

2.2:Long

对于long,其占用8个字节,但是如果是其包装类型Long,则要占用(对象头标记字8字节+对象头class指针4字节+对象内容int本身8字节+外部alignment4字节=24字节),如下也可以验证:

C:\Users\Administrator>jmap -histo 7212 | findstr "java.lang.Long"
  28:           100           2400  java.lang.Long

可以看到每个的大小是2400/100=24字节

所以在实际编码中,能使用基本数据类型的还是使用基本数据类型,因为包装数据类型的内存占用量相比于基础数据类型要多出几倍。

3:数组

以int数组为例,对于一维的int数组,其结构如下:

在这里插入图片描述

因此如果是int[256]则大小是(标记字8字节+class指针4字节+数组长度4字节+内容256*4=1040字节)

对于二维数组int[dim1][dim2]而言,每个int[dim2]都是使用一个额外的对象来表示的,因此会占用更大的内存空间,如果我们将int[256]使用int[128][2]来表示的话,则结构如下图:

在这里插入图片描述

每个元素的大小是(指针4字节+int[2]对象24字节=28字节),共128个,所以大小是128*28=3584字节,再加上第一纬的标记头8字节,class指针4字节,数组长度4字节,共3600字节,可看到存储相同量的元素,int[128][2]比int[256]多占用了(3600-1040=2560字节)的内存空间。

所以,在实际工作中,尽量避免使用多维数组,有需要也尽量使用一维数组来实现,将一维数组折叠一下就行了。

4:String

想要分析处String占用的内存大小,需要先来看下String的定义(只列出会占用堆内存的)

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
}

结构如下:

在这里插入图片描述

则一个空string占用的大小是36字节,如果是不包括String对象头的话,则大小是24字节。

5:普通的类

如下的类:

public class EmptyObject {
    int a;
    byte b;
    Integer c = new Integer(10);
}

内存结构如下:

在这里插入图片描述

所以总大小是(标记字8字节+class指针4字节+int a 4字节+byte b 1字节+Integer c指针 4字节=21字节),再对齐,因此是24字节,如下验证:

public class Main {

    public static void main(String[] args) throws InterruptedException {
        List<EmptyObject> oneHundredList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            oneHundredList.add(new EmptyObject());
        }
        Thread.sleep(Integer.MAX_VALUE);
    }
}
:\Users\Administrator>jps -l
13200 dongshi.daddy.objectsize.Main
12692
6824 sun.tools.jps.Jps
14892
19580 org.jetbrains.idea.maven.server.RemoteMavenServer36
3308 org.jetbrains.jps.cmdline.Launcher

C:\Users\Administrator>jmap -histo 13200 | findstr "EmptyOb"
  28:           100           2400  dongshi.daddy.objectsize.EmptyObject

可以看到每个确实是24字节。

写在后面

参考文章列表

Java中的String到底占用多大的内存空间?带你一步步验证!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值