25

一个Java对象到底占用多大内存?

 4 years ago
source link: https://www.tuicool.com/articles/2YNr2eM
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近在调研MAT和VisualVM源码实现,遇到一个可疑问题,两者计算出来的对象大小不一致,才有了这样疑惑。

一个Java对象到底占用多大内存?

ANbIBve.jpg!web

为了复现这个问题,准备了4个最简单类:

当然了,再来个主函数:

本地的执行环境是64位的JDK8,且使用默认的启动参数,运行之后通过 jmap-dump 命令生成dump文件,分别用MAT和VisualVM打开。

MAT

iaiiaiY.jpg!web

通过MAT打开,可以发现ABD对象大小都是16字节,而C对象大小为24字节

VisualVM

FVJNviF.jpg!web

通过Vis打开,可以发现其显示的大小和MAT有蛮大的差别。

ZVnYfa7.jpg!web

好奇怪,哪个是对的?

要回答这个问题,首先得清楚的知道JVM中对象的内存布局。

在Hotspot中,一个对象包含3个部分: 对象头、实例数据和对齐填充

对象头

这里不讲对象头是个什么东西,感兴趣的同学可以看我的其它文章。对象头的大小一般和系统的位数有关,也和启动参数 UseCompressedOops 有关:

  • 32位系统,占用 8 字节

  • 64位系统,开启  UseCompressedOops 时,占用 12 字节,否则是16字节

实例数据

原生类型的内存占用情况如下:

  • boolean 1个字节

  • byte 1 字节

  • short 2 字节

  • char 2 字节

  • int 4 字节

  • float 4 字节

  • long 8 字节

  • double 8 字节

引用类型的内存占用和系统位数以及启动参数 UseCompressedOops 有关

  • 32位系统占4字节

  • 64位系统,开启  UseCompressedOops 时,占用4字节,否则是8字节

对齐填充

在Hotspot中,为了更加容易的管理内存,一般会 使用8字节进行对齐

意思是每次 分配的内存大小一定是8的倍数 ,如果对象头+实例数据的值不是8的倍数,那么会重新计算一个较大值,进行分配。

结果

有了对象各部分的内存占用大小,可以很轻松的计算出ABCD各对象在64位系统,且开启 UseCompressedOops 参数时的大小。

  • A对象只包含一个对象头,大小占12字节,不是8的倍数,需要4字节进行填充,一共占16字节

  • B对象包含一个对象头和int类型,12+4=16,正好是8的倍数,不需要填充。

  • C对象包含一个对象头和long类型,12+8=20,不是8的倍数,需要4个字节进行填充,占24字节

  • D对象包含一个对象头和引用类型,12+4=16,正好是8的倍数,不需要填充。

可以得出, VisualVM的显示结果有点问题 ,主要因为以下两点:

  • 首先,没有考虑是否开启  UseCompressedOops;

  • 其次,没有考虑内存对齐填充的情况;

感兴趣的同学,可以动手实践一下,这样可以加深对象内存布局的理解。

经过这段时间对MAT和VisualVM的源码研究,发现MAT的功能不是强大一点点,建议大家以后尽量使用MAT。

【阿飞的博客】 公众号二维码

↓↓↓↓

UZ7B7n7.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK