组成类的内存
一个类占用的内存包括对象头部、成员变量引用地址(包括基本数据类型和对象引用)。在 32 位的 JVM 上,一个对象引用占用 4 个字节;在 64 位上,占用 8 个字节。
对象头包含三部分。第一部分是mark word,包括hashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。第二部分是类元信息也就是 Class 对象的引用。第三部分是数组长度,只有在数组对象的情况下会有。
对象填充
Java 对象以 8 字节的倍数填充,如果对象占用 14 个字节,那么自动填充到 16 字节。
指针压缩
Jdk 6 开始默认开启,启动参数是 -XX:+UseCompressedOops。64 位的寻址位数是 8 字节,32 位是 4 字节,在未压缩前 64 位 JVM 的对象引用地址是 8 字节,压缩以后是 4 字节。但也不是全部都压缩,这些是会压缩的:
- 每个Class的属性指针(静态成员变量);
- 每个对象的属性指针;
- 普通对象数组的每个元素指针。
试试各种对象的大小
64 位 JDK 8 下测试,分开启和不开启指针压缩,用 java.lang.instrument.Instrumentation 计算 shallow 内存大小。
先测开启指针压缩的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| size of array object: 16 // 16 = 8(_mark) + 4 (compressed_klass) + 4(size)
size of array object with 1 int: 24 // 24 = 8(_mark) + 4 (compressed_klass) + 4(size) + 1 * 4(int) + 4(gap)
size of empty object: 16 // 16 = 8(_mark) + 4 (compressed_klass) + 4(gap)
size of object with int: 16 // 16 = 8(_mark) + 4 (compressed_klass) + 4(int)
size of object with 2 int: 24 // 24 = 8(_mark) + 4 (compressed_klass) + 2 * 4(int) + 4(gap)
|
然后是未开启指针压缩的,变化就是 class 引用会增大到 8 字节:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| size of array object: 24 // 24 = 8(_mark) + 8 (compressed_klass) + 4(size) + 4(gap)
size of array object with 1 int: 32 // 24 = 8(_mark) + 8 (compressed_klass) + 4(size) + 1 * 4(int)
size of empty object: 16 // 16 = 8(_mark) + 8 (compressed_klass)
size of object with int: 24 // 24 = 8(_mark) + 8 (compressed_klass) + 4(int) + 4(gap)
size of object with 2 int: 24 // 24 = 8(_mark) + 8 (compressed_klass) + 2 * 4(int)
|
开启压缩时试试 String 类型:
1 2
| private final char value[]; private int hash;
|
1 2 3 4 5
| size of empty String: 24 // 24 = 8(_mark) + 4 (compressed_klass) + 4(value[]) + 4(int) + 4(gap)
size of abc String: 24 // 24 = 8(_mark) + 4 (compressed_klass) + 4(value[]) + 4(int) + 4(gap)
|
因为统计的是浅内存大小,所以 char 数组内元素不计算在内。
如何计算对象的 deep 内存大小
http://loveshisong.cn/编程技术/2015-06-11-java对象的内存大小.html