JVM技术专题 Java各种类型对象占用内存情况分析「上篇」

Posted 浩宇の天尚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM技术专题 Java各种类型对象占用内存情况分析「上篇」相关的知识,希望对你有一定的参考价值。

前言

只有当你到了一定层次,需要了解JVM内部运行机制,或者高并发多线程下,你写的代码对内存有影响,你想做性能优化当你想深入了解java对象在内存中,如何存储,或者每个对象占用多大空间时。

内存公式

Java对象的内存布局=对象头(Header)+实例数据(Instance Data)+补齐填充(Padding)

补齐填充

Java对象占用空间是8字节对齐的,即所有Java对象占用bytes数必须是8的倍数

Shallow Size

  1. 对象自身占用的内存大小,不包括它引用的对象。

  2. 针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。当然这里面还会包括一些java语言特性的数据存储单元。

  3. 针对数组类型的对象,它的大小是数组元素对象的大小总和。

Retained Size

Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C,C就是间接引用)

换句话说,Retained Size就是当前对象被GC后,从Heap上总共能释放掉的内存

不过,释放的时候还要排除被GC Roots直接或间接引用的对象。他们暂时不会被被当做Garbage。

接下来用JProfiler验证:

  1. 新建一个空对象,观察空对象内存占用
public class TestObject 

对象占用内存 16byte,如图:

结论

一般自建空对象占用内存 16Byte,16byte = 12Byte(Header) + 4Byte(Padding)

  1. 在TestObj中新增一个int属性,观察对象内存占用
public class TestObj 
    private int i;

对象占用内存16byte,如图

结论

int 占用 4byte,16byte = 12byte(Header) + 4byte(int)+0byte(padding)

  1. 在TestObj中新增一个 long 属性,观察对象内存占用
public class TestObj 
    private long i;

对象占用内存 24b,如图

结论

long 占用 8byte, 24byte = 12(Header) + 8(long) + 4(Padding)

其余基本类型可以参照以上自行验证,原理一样

包装类型占用

  • 包装类(Boolean/Byte/Short/Character/Integer/Long/Double/Float)占用内存的大小 = 对象头大小 + 底层基础数据类型的大小

  • 包装类和其他引用类一样,会产生一个引用(reference)

  1. 在TestObj中新增一个Integer属性,观察对象内存占用
public class TestObj 
   private Integer  i =128;

对象占用内存 32b,如图

结论

Integer 占用16b, 32 = 12 (Header) + 16(Integer) + 4(reference)

特别的:-128~127在常量池,只占用4b,且不产生引用(reference)

  1. 在TestObj中新增一个 Long属性,观察对象内存占用
public class TestObj 
   private Long  l = new Long(1);

对象占用内存 40b,如图

结论

Long 占用 24b, 40 = 12 (Header) + 24(Long) + 4(reference)

其余包装类型可以参照以上自行验证,原理一样

基本类型数组占用

64位机器上,数组对象的对象头占用24 bytes,启用压缩后占用16字节。比普通对象占用内存多是因为需要额外的空间存储数组的长度(普通16b-12b)

对象数组本身的大小=数组对象头 + length * 存放单个元素大小

在TestObj中新增一个 char[] 属性,观察对象内存占用

public class TestObj 
   private char[] c = 'a','b','c';

char[] c占用内存 40b,如图

结论

char[3] 占用 24b, 24 = 40 - 16,24 = 16(Header) + 3 * 2(char) + 2(Padding)

封装类型数组占用

封装类型数组比基本类型的数组,需要多管理元素的引用

对象数组本身的大小=数组对象头+length * 引用指针大小 + length * 存放单个元素大小。

在TestObj中新增一个 Integer[] 属性,观察对象内存占用。

public class TestObj 
    private Integer[] i = 128,129,130;

Integer[] i占用内存 80b,如图

结论

Integer[3] 占用 80b, 80 = 96 - 16, 80 = 16(Header) + 3 * 4 (reference)+ 3 * 16(Integer) +4(padding)

String占用内存

在TestObj中新增一个空String属性,观察对象内存占用。

public class TestObj 
    private String s = new String("");

对象占用内存 40b,如图

结论

String本身占用24byte,24=40-16,也就是说空"",也需要16byte

注意

这里为什么要写String s = new String(“”)?请自己思考,不写会怎么样?

答:如果写成String s = “”,是不会再堆中开辟内存的,也就看不到String占用的空间,你看到的将会是下面的,至于为什么,都是因为final。

以上是关于JVM技术专题 Java各种类型对象占用内存情况分析「上篇」的主要内容,如果未能解决你的问题,请参考以下文章

JVM技术专题内存问题分析和故障排查规划指南「实战篇」

JVM技术专题深入挖掘Java对象的内存结构「原理篇」

如何查看Java对象占用JVM内存大小

Java技术专题-JVM研究系列(24)深入挖掘Java对象的内存结构

JVM技术专题「原理专题」全流程分析Java对象的创建过程及内存布局

JVM技术专题「原理专题」深入剖析Java对象内存分配及跨代引用分析