起源:在优化一段逻辑的时候用到了本地缓存,本地缓存里面有个参数是设置最大对象数量。设置的时候暂时无法评估需要多少个,那退而求其次就先搞明白将要放进去的对象一个多大。
看过之前一个文章,https://mp.weixin.qq.com/s/XLCHuMiQ1nFWxLN9urx3aA new一个对象占用了多少字节中提到了一个查看对象大小工具jol,那么就今天就拿来玩玩。
JOL是用来查看对象内存分布及对象内不同部分的占用字节数,官网地址http://openjdk.java.net/projects/code-tools/jol/,可以发现它是openJDK提供的一个工具。其他的工具后面有兴趣再研究。
JOL工具的maven依赖如下
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.14</version>
</dependency>
一个被观察的对象
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@Data
@Builder
public class CacheObj implements Serializable {
private Long id;
private String name;
private Date date;
private List<CacheObj> cacheObjs;
}
JUnit的测试方法
@Test
public void guava() throws Exception{
CacheObj tom = CacheObj.builder().id(1L).name("tom").date(new Date()).build();
List<CacheObj> aa = new ArrayList<>();
aa.add(CacheObj.builder().id(2L).name("jerry").date(new Date()).build());
tom.setCacheObjs(aa);
log.info("内部信息:[{}]", ClassLayout.parseInstance(tom).toPrintable());
log.info("外部信息[{}]", GraphLayout.parseInstance(tom).toPrintable());
log.info("totalSize[{}]", GraphLayout.parseInstance(tom).totalSize());
}
测试输出日志如下
内部信息
它包含了头信息,数据,填充部分。
- 前三行是对象头信息,包含了Mark Word, Class Pointer,这里是8+4,有兴趣的可以了解下JVM的指针压缩
- 再四行是数据信息,包含了4个字段,为什么4个字段都是4个字节?因为这里存的都是引用类型,附,java类型的内存大小
- 最后一行4个字节填充
这个对象总共32字节
问:为什么4个字段都是4个字节?
答:这里存的都是引用类型,附,java类型的内存大小,
附:java数据类型占用内存大小
对象类型 内存大小 boolean 1 byte
1 short 2 char 2 int 4 float 4 long 8 double 8
引用类型 在 32 位系统上每个占用 4bytes(即32bit, 才能管理 2^32=4G 的内存), 在 64 位系统上每个占用 8bytes(开启压缩为 4 bytes)
外部信息
包含了当前对象信息,及当前对象引用对象的信息。
从截图上可以看到
PATH下 “.xxx”代表了当前对象属性信息。
VALUE 是指它存储的数据内容
something else 是指存放了其他内容,无需关心
.elementData 是指列表
.elementData[0] 代表了数组的第一个对象
手工进行加和,或者totalSize可以知道,整个tom对象占用了344字节的大小。
用另一篇博文(https://blog.csdn.net/wenniuwuren/article/details/50958892)的图会更加清楚整个对象的计算(ps:对象是原博使用的对象)
延伸:数组的内存包含哪些部分?及它的存储计算方式?
后记:仔细验证会发现些有趣的东西
- 如果第二个CacheObj的name也用“tom”,会发现最终的内存中只有一个“tom”的char数组,这印证了字符串常量池的使用
- arrayList初始化的数组个数是10个,没有数据的位置上以null填充
- 如果查看String对象内存分布,实例数据包含了char[]数组的地址和int类型hash值
参考:
https://blog.csdn.net/wenniuwuren/article/details/50958892
https://blog.csdn.net/shihlei/article/details/84914901