一个Java对象到底占用多大内存
2015-09-22 16:24
676 查看
最近在读《深入理解Java虚拟机》,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存?
在网上搜到了一篇博客讲的非常好:http://yueyemaitian.iteye.com/blog/2033046,里面提供的这个类也非常实用:
View
Code
大家可以用这个代码边看边验证,注意的是,运行这个程序需要通过javaagent注入Instrumentation,具体可以看原博客。我今天主要是总结下手动计算Java对象占用字节数的基本规则,做为基本的技能必须get√,希望能帮到和我一样的Java菜鸟。
在介绍之前,简单回顾下,Java对象的内存布局:对象头(Header),实例数据(Instance Data)和对齐填充(Padding),详细的可以看我的读书笔记。另外:不同的环境结果可能有差异,我所在的环境是HotSpot虚拟机,64位Windwos。
下面进入正文:
对象头在32位系统上占用8bytes,64位系统上占用16bytes。
![](https://images0.cnblogs.com/i/288950/201405/281942192132905.png)
![](https://images0.cnblogs.com/i/288950/201405/281942312133838.png)
原生类型(primitive
type)的内存占用如下:
reference类型在32位系统上每个占用4bytes, 在64位系统上每个占用8bytes。
HotSpot的对齐方式为8字节对齐:
(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8
对象占用的内存大小收到VM参数UseCompressedOops的影响。
开启(-XX:+UseCompressedOops)对象头大小为12bytes(64位机器)。
A对象占用内存情况:
关闭指针压缩: 16+4=20不是8的倍数,所以+padding/4=24
![](https://images0.cnblogs.com/i/288950/201405/281948383224150.png)
开启指针压缩: 12+4=16已经是8的倍数了,不需要再padding。
![](https://images0.cnblogs.com/i/288950/201405/281948446666546.png)
64位机器上reference类型占用8个字节,开启指针压缩后占用4个字节。
B2对象占用内存情况:
关闭指针压缩: 16+4+8=28不是8的倍数,所以+padding/4=32
![](https://images0.cnblogs.com/i/288950/201405/281950335883037.png)
开启指针压缩: 12+4+4=20不是8的倍数,所以+padding/4=24
![](https://images0.cnblogs.com/i/288950/201405/281950405253616.png)
64位机器上,数组对象的对象头占用24个字节,启用压缩之后占用16个字节。之所以比普通对象占用内存多是因为需要额外的空间存储数组的长度。
先考虑下new Integer[0]占用的内存大小,长度为0,即是对象头的大小:
未开启压缩:24bytes
![](https://images0.cnblogs.com/i/288950/201405/281951361666018.png)
开启压缩后:16bytes
![](https://images0.cnblogs.com/i/288950/201405/281951534639534.png)
接着计算new Integer[1],new Integer[2],new Integer[3]和new Integer[4]就很容易了:
未开启压缩:
![](https://images0.cnblogs.com/i/288950/201405/281952456196066.png)
开启压缩:
![](https://images0.cnblogs.com/i/288950/201405/281952563533487.png)
拿new Integer[3]来具体解释下:
未开启压缩:24(对象头)+8*3=48,不需要padding;
开启压缩:16(对象头)+3*4=28,+padding/4=32,其他依次类推。
自定义类的数组也是一样的,比如:
new B3[3]占用的内存大小:
未开启压缩:48
开启压缩后:32
计算复合对象占用内存的大小其实就是运用上面几条规则,只是麻烦点。
直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小。
未开启压缩:16(对象头)+4(ba)+8(as引用的大小)+padding/4=32
开启压缩:12+4+4+padding/4=24
递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小。
递归计算复合对象占用的内存的时候需要注意的是:对齐填充是以每个对象为单位进行的,看下面这个图就很容易明白。
![](https://images0.cnblogs.com/i/288950/201405/281956463229130.png)
现在我们来手动计算下C对象占用的全部内存是多少,主要是三部分构成:C对象本身的大小+数组对象的大小+B对象的大小。
未开启压缩:
(16 + 4 + 8+4(padding)) + (24+ 8*3) +(16+8)*3 = 152bytes
开启压缩:
(12 + 4 + 4 +4(padding)) + (16 + 4*3 +4(数组对象padding)) + (12+8+4(B对象padding))*3=
128bytes
大家有兴趣的可以试试。
实际工作中真正需要手动计算对象大小的场景应该很少,但是个人觉得做为基础知识每个Java开发人员都应该了解,另外:对自己写的代码大概占用多少内存,内存中是怎么布局的应该有一个直觉性的认识。
作者:zhanjindong
出处:http://www.cnblogs.com/zhanjindong/p/3757767.html
关于instrument请参考:http://my.oschina.net/xianggao/blog/362511
关于Java对象的内存布局还有一篇文章讲解得也很清晰:http://blog.csdn.net/aaa1117a8w5s6d/article/details/8254922关于指针压缩,可以参考: http://www.th7.cn/Program/java/201507/497634.shtml.
在网上搜到了一篇博客讲的非常好:http://yueyemaitian.iteye.com/blog/2033046,里面提供的这个类也非常实用:
View
Code
大家可以用这个代码边看边验证,注意的是,运行这个程序需要通过javaagent注入Instrumentation,具体可以看原博客。我今天主要是总结下手动计算Java对象占用字节数的基本规则,做为基本的技能必须get√,希望能帮到和我一样的Java菜鸟。
在介绍之前,简单回顾下,Java对象的内存布局:对象头(Header),实例数据(Instance Data)和对齐填充(Padding),详细的可以看我的读书笔记。另外:不同的环境结果可能有差异,我所在的环境是HotSpot虚拟机,64位Windwos。
下面进入正文:
对象头
对象头在32位系统上占用8bytes,64位系统上占用16bytes。![](https://images0.cnblogs.com/i/288950/201405/281942192132905.png)
![](https://images0.cnblogs.com/i/288950/201405/281942312133838.png)
实例数据
原生类型(primitivetype)的内存占用如下:
Primitive Type | Memory Required(bytes) |
boolean | 1 |
byte | 1 |
short | 2 |
char | 2 |
int | 4 |
float | 4 |
long | 8 |
double | 8 |
对齐填充
HotSpot的对齐方式为8字节对齐:(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8
指针压缩
对象占用的内存大小收到VM参数UseCompressedOops的影响。
1)对对象头的影响
开启(-XX:+UseCompressedOops)对象头大小为12bytes(64位机器)。static class A { int a; }
A对象占用内存情况:
关闭指针压缩: 16+4=20不是8的倍数,所以+padding/4=24
![](https://images0.cnblogs.com/i/288950/201405/281948383224150.png)
开启指针压缩: 12+4=16已经是8的倍数了,不需要再padding。
![](https://images0.cnblogs.com/i/288950/201405/281948446666546.png)
1) 对reference类型的影响
64位机器上reference类型占用8个字节,开启指针压缩后占用4个字节。static class B2 { int b2a; Integer b2b; }
B2对象占用内存情况:
关闭指针压缩: 16+4+8=28不是8的倍数,所以+padding/4=32
![](https://images0.cnblogs.com/i/288950/201405/281950335883037.png)
开启指针压缩: 12+4+4=20不是8的倍数,所以+padding/4=24
![](https://images0.cnblogs.com/i/288950/201405/281950405253616.png)
数组对象
64位机器上,数组对象的对象头占用24个字节,启用压缩之后占用16个字节。之所以比普通对象占用内存多是因为需要额外的空间存储数组的长度。先考虑下new Integer[0]占用的内存大小,长度为0,即是对象头的大小:
未开启压缩:24bytes
![](https://images0.cnblogs.com/i/288950/201405/281951361666018.png)
开启压缩后:16bytes
![](https://images0.cnblogs.com/i/288950/201405/281951534639534.png)
接着计算new Integer[1],new Integer[2],new Integer[3]和new Integer[4]就很容易了:
未开启压缩:
![](https://images0.cnblogs.com/i/288950/201405/281952456196066.png)
开启压缩:
![](https://images0.cnblogs.com/i/288950/201405/281952563533487.png)
拿new Integer[3]来具体解释下:
未开启压缩:24(对象头)+8*3=48,不需要padding;
开启压缩:16(对象头)+3*4=28,+padding/4=32,其他依次类推。
自定义类的数组也是一样的,比如:
static class B3 { int a; Integer b; }
new B3[3]占用的内存大小:
未开启压缩:48
开启压缩后:32
复合对象
计算复合对象占用内存的大小其实就是运用上面几条规则,只是麻烦点。
1)对象本身的大小
直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小。static class B { int a; int b; } static class C { int ba; B[] as = new B[3]; C() { for (int i = 0; i < as.length; i++) { as[i] = new B(); } } }
未开启压缩:16(对象头)+4(ba)+8(as引用的大小)+padding/4=32
开启压缩:12+4+4+padding/4=24
2)当前对象占用的空间总大小
递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小。递归计算复合对象占用的内存的时候需要注意的是:对齐填充是以每个对象为单位进行的,看下面这个图就很容易明白。
![](https://images0.cnblogs.com/i/288950/201405/281956463229130.png)
现在我们来手动计算下C对象占用的全部内存是多少,主要是三部分构成:C对象本身的大小+数组对象的大小+B对象的大小。
未开启压缩:
(16 + 4 + 8+4(padding)) + (24+ 8*3) +(16+8)*3 = 152bytes
开启压缩:
(12 + 4 + 4 +4(padding)) + (16 + 4*3 +4(数组对象padding)) + (12+8+4(B对象padding))*3=
128bytes
大家有兴趣的可以试试。
实际工作中真正需要手动计算对象大小的场景应该很少,但是个人觉得做为基础知识每个Java开发人员都应该了解,另外:对自己写的代码大概占用多少内存,内存中是怎么布局的应该有一个直觉性的认识。
作者:zhanjindong
出处:http://www.cnblogs.com/zhanjindong/p/3757767.html
关于instrument请参考:http://my.oschina.net/xianggao/blog/362511
关于Java对象的内存布局还有一篇文章讲解得也很清晰:http://blog.csdn.net/aaa1117a8w5s6d/article/details/8254922关于指针压缩,可以参考: http://www.th7.cn/Program/java/201507/497634.shtml.
相关文章推荐
- 一个Java对象到底占用多大内存
- Java for LeetCode 230 Kth Smallest Element in a BST
- Test5.17
- Java正则表达式入门
- Struts 2 的国际化
- java教程
- java多线程 synchronized volatile Atomic LOCK的使用
- test5.16
- Spring 代码知识查缺补漏
- FileChannel-Java 复制文件的高效方法
- eclipse调试java程序的九个技巧
- Spring Security hello world example
- JavaWeb开发中关于分页的实现
- Spring内部bean无法通过id获取
- week 3-1,3-2 java_se_1
- test5.15
- java 集合类
- 《Java编程思想》学习记录仪四 -- Java数组和作用域初步认识(第二章一切都是对象)
- Java虚拟机调优工具 jstack
- java 连接数据库mysql的语句怎么写