【转】一个Java对象到底占多大内存?
最近在读《深入理解Java虚拟机》,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存?
在网上搜到了一篇博客讲的非常好:http://yueyemaitian.iteye.com/blog/2033046,里面提供的这个类也非常实用:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
import java.lang.instrument.Instrumentation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashSet; import java.util.Set; /** * 对象占用字节大小工具类 * * @author tianmai.fh * @date 2014-03-18 11:29 */ public class SizeOfObject { static Instrumentation inst; public static void premain(String args, Instrumentation instP) { inst = instP; } /** * 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、</br> * 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;</br> * 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 </br> * * @param obj * @return */ public static long sizeOf(Object obj) { return inst.getObjectSize(obj); } /** * 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小 * * @param objP * @return * @throws IllegalAccessException */ public static long fullSizeOf(Object objP) throws IllegalAccessException { Set<Object> visited = new HashSet<Object>(); Deque<Object> toBeQueue = new ArrayDeque<Object>(); toBeQueue.add(objP); long size = 0L; while (toBeQueue.size() > 0) { Object obj = toBeQueue.poll(); //sizeOf的时候已经计基本类型和引用的长度,包括数组 size += skipObject(visited, obj) ? 0L : sizeOf(obj); Class<?> tmpObjClass = obj.getClass(); if (tmpObjClass.isArray()) { //[I , [F 基本类型名字长度是2 if (tmpObjClass.getName().length() > 2) { for (int i = 0, len = Array.getLength(obj); i < len; i++) { Object tmp = Array.get(obj, i); if (tmp != null) { //非基本类型需要深度遍历其对象 toBeQueue.add(Array.get(obj, i)); } } } } else { while (tmpObjClass != null) { Field[] fields = tmpObjClass.getDeclaredFields(); for (Field field : fields) { if (Modifier.isStatic(field.getModifiers()) //静态不计 || field.getType().isPrimitive()) { //基本类型不重复计 continue; } field.setAccessible(true); Object fieldValue = field.get(obj); if (fieldValue == null) { continue; } toBeQueue.add(fieldValue); } tmpObjClass = tmpObjClass.getSuperclass(); } } } return size; } /** * String.intern的对象不计;计算过的不计,也避免死循环 * * @param visited * @param obj * @return */ static boolean skipObject(Set<Object> visited, Object obj) { if (obj instanceof String && obj == ((String) obj).intern()) { return true; } return visited.contains(obj); } } |
大家可以用这个代码边看边验证,注意的是,运行这个程序需要通过javaagent注入Instrumentation,具体可以看原博客。我今天主要是总结下手动计算Java对象占用字节数的基本规则,做为基本的技能必须get√,希望能帮到和我一样的Java菜鸟。
在介绍之前,简单回顾下,Java对象的内存布局:对象头(Header),实例数据(Instance Data)和对齐填充(Padding),详细的可以看我的读书笔记。另外:不同的环境结果可能有差异,我所在的环境是HotSpot虚拟机,64位Windwos。
下面进入正文:
对象头
对象头在32位系统上占用8bytes,64位系统上占用16bytes。


实例数据
原生类型(primitive type)的内存占用如下:
| Primitive Type | Memory Required(bytes) |
| boolean | 1 |
| byte | 1 |
| short | 2 |
| char | 2 |
| int | 4 |
| float | 4 |
| long | 8 |
| double | 8 |
reference类型在32位系统上每个占用4bytes, 在64位系统上每个占用8bytes。
对齐填充
HotSpot的对齐方式为8字节对齐:
(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8
指针压缩
对象占用的内存大小收到VM参数UseCompressedOops的影响。
1)对对象头的影响
开启(-XX:+UseCompressedOops)对象头大小为12bytes(64位机器)。
|
1
2
3
|
static class A { int a; } |
A对象占用内存情况:
关闭指针压缩: 16+4=20不是8的倍数,所以+padding/4=24

开启指针压缩: 12+4=16已经是8的倍数了,不需要再padding。

2) 对reference类型的影响
64位机器上reference类型占用8个字节,开启指针压缩后占用4个字节。
|
1
2
3
4
|
static class B2 { int b2a; Integer b2b;} |
B2对象占用内存情况:
关闭指针压缩: 16+4+8=28不是8的倍数,所以+padding/4=32

开启指针压缩: 12+4+4=20不是8的倍数,所以+padding/4=24

数组对象
64位机器上,数组对象的对象头占用24个字节,启用压缩之后占用16个字节。之所以比普通对象占用内存多是因为需要额外的空间存储数组的长度。
先考虑下new Integer[0]占用的内存大小,长度为0,即是对象头的大小:
未开启压缩:24bytes

开启压缩后:16bytes

接着计算new Integer[1],new Integer[2],new Integer[3]和new Integer[4]就很容易了:
未开启压缩:

开启压缩:

拿new Integer[3]来具体解释下:
未开启压缩:24(对象头)+8*3=48,不需要padding;
开启压缩:16(对象头)+3*4=28,+padding/4=32,其他依次类推。
自定义类的数组也是一样的,比如:
|
1
2
3
4
|
static class B3 { int a; Integer b; } |
new B3[3]占用的内存大小:
未开启压缩:48
开启压缩后:32
复合对象
计算复合对象占用内存的大小其实就是运用上面几条规则,只是麻烦点。
1)对象本身的大小
直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小; 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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)当前对象占用的空间总大小
递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小。
递归计算复合对象占用的内存的时候需要注意的是:对齐填充是以每个对象为单位进行的,看下面这个图就很容易明白。

现在我们来手动计算下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开发人员都应该了解,另外:对自己写的代码大概占用多少内存,内存中是怎么布局的应该有一个直觉性的认识。

相关文章
- Java对象的序列化与反序列化
- 以日为始:Dalvik vs ASM.JS vs 本地代码——大比拼
- 解码OutOfMemoryError:PermGen Space
- Java8中的java.util.Random类
- 更好的异常处理:实现JUnit API
- 构建Java Agent,而不是使用框架
- Java命令学习系列(7):Javap
- Java集合框架:LinkedHashMap
- Java应用架构的演化之路
- 记一次诡异的 ssh 互信免密码登录失败
【转】一个Java对象到底占多大内存?的更多相关文章
- 一个Java对象到底占多大内存
最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...
- 一个Java对象到底占多大内存?(转)
最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...
- 一个Java对象到底占用多大内存?
最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...
- 一个Java对象到底占用多大内存
在网上搜到了一篇博客讲的非常好,里面提供的这个类也非常实用: import java.lang.instrument.Instrumentation; import java.lang.reflect ...
- 一个 Java 对象到底有多大?
阅读本文大概需要 2.8 分钟. 出处:http://u6.gg/swLPg 编写 Java 代码的时候,大多数情况下,我们很少关注一个 Java 对象究竟有多大(占据多少内存),更多的是关注业务与逻 ...
- 如何获取一个Java对象所占内存大小
新建一个maven工程 我们先在IDEA中新建一个名为ObjectSizeFetcherAgent的maven工程,如下图: 在maven项目中的pom.xml中新增一个打jar包的插件,如下: &l ...
- 一个PHP数组能占多大内存
最近用PHP读取一个大文件把相关数据存放到数组中,之后处理并输出, 读取过程中发现占用内存很大, 于是很好奇这个问题. 简单的写一个代码 <?php $m1 = memory_get_usage ...
- 高端面试必备:一个Java对象占用多大内存
这个问题一般会出现在稍微高端一点的 Java 面试环节.要求面试者不仅对 Java 基础知识熟悉,更重要的是要了解内存模型. Java 对象模型 HotSpot JVM 使用名为 oops (Ordi ...
- 如何计算Java对象所占内存的大小
[ 简单总结: 随便一个java项目,引入jar包: lucene-core-4.0.0.jar 如果是 maven项目,直接用如下依赖: <dependency> <groupId ...
随机推荐
- golang基础-WaitGroup、kafka消费者
kafka消费者 以下博客是通过生产者创建.发送消息至kafka 博客链接 现在我们站在消费者的角度,来进行收取消息 package main import ( "fmt" &qu ...
- DedeCms最新文章显示红色日期或加上new字或new小图片
DedeCMS发布的文章显示红色日期或加上new字或new小图片,给近三天(或当天)发布的文章显示红色日期或加上new字或new小图片等,都是围绕pubdate做文章,写扩展的. 1.红色的日期 ...
- FileReader、 FileWriter、readLine()和newLine()、LineNumberReader(二十一)
1.字符流FileReader * 1.字符流是什么 * 字符流是可以直接读写字符的IO流 * 字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出. ...
- MongoDB3.6.3 windows安装配置、启动
1.官网下载MongoDB的安装包 2.安装中一直Next即可安装成功,不过需要注意的是: 可以自定义安装,选择安装路径 值得注意的还有,安装中因为下载compass十分缓慢.把下面默认选中的勾去掉 ...
- Python return语句用法分析
return 语句 程序运行到所遇到的第一个return即返回(退出def块),不会再运行第二个return. 要返回两个数值,写成一行即可: def a(x,y): if x==y: return ...
- oracle自动统计信息
在11g中,默认自动收集统计信息的时间为晚上10点(周一到周五,4个小时),早上6点(周六,周日,20个小时), select a.window_name, a.repeat_interval,a ...
- 【扬中集训 DAY4T1】跳马
[题目链接] 点击打开链接 [算法] 数据范围很大,显然暴力是不能通过的 我们可以先打表,发现答案为 : 41 109 205 325 473 649 853 1085 1345 观察数列的差 68 ...
- C:static 关键字、静态变量、跨类访问数据
static 在OC中的使用 参考1 参考2 参考3 参保4 参考5 跨类访问成员 参考 +号方法就是类方法(静态方法),说明不用创建对象,可以直接通过类型去直接调用这个方法,在OC ...
- HDU-5551 Huatuo's Medicine
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Submission( ...
- k8s-容器资源需求、资源限制及HeapSter-二十二
一.容器资源需求.资源限制 资源需求.资源限制:指的是cpu.内存等资源: 资源需求.资源限制的两个关键字: request:需求,最低保障,在调度时,这个节点必须要满足request需求的资源大小: ...