Bootstrap

计算Java集合占用的空间【详解】

以ArrayList为例,假设集合元素类型是Person类型,假设集合容量为10,目前有两个person对象{name:“Jack”,age=12} {name:“Tom”,age=14}

public class Person{
    private String name;
    private int age;
}

估算Person对象占用的大小:

  • 对象头:12字节(开启压缩指针)

  • 字段:对象中保存的是基本数据类型的字段数据以及引用类型字段的引用,4+4=8字节

如果Person对象固定占用20字节,并且ArrayList的容量是10,那么我们可以计算ArrayList本身及其内部数组所占用的空间。

ArrayList对象本身的内存估算

  • 对象头:12字节(运行时元数据8字节,类型指针Oop4字节)
  • int类型的变量(例如size, modCount等):每个int 4字节,假设至少有两个这样的变量,那么总共8字节
  • 数组引用:4字节

所以,ArrayList对象本身大约占用:

12 (对象头) + 8 (int类型变量) + 4 (数组引用) = 24字节

内部数组的内存估算

  • 每个Person对象引用:4字节
  • 数组的容量是10,即使只有两个Person对象,数组的实际大小还是10,因为ArrayList会预留一定的容量。

因此,内部数组占用的空间为:

10 * 4 (每个引用4字节) = 40字节

总计

ArrayList对象加上其内部数组的总空间估计为:

24 (ArrayList对象) + 40 (内部数组) = 64字节

这是ArrayList结构本身和它的内部数组所占用的空间。需要注意的是,这不包括实际存储在ArrayList中的Person对象的大小。由于你提到每个Person对象固定占用20字节,并且现在有两个Person对象,这些对象将额外占用:

2 * 20 = 40字节

综上所述,整个ArrayList及其包含的两个Person对象总共占用的空间约为:

64 (ArrayList和数组) + 40 (两个Person对象) = 104字节

说明:

集合/数组中存储的如果是基本数据类型则存储实际的数据,如果是引用类型,则只存储对象的引用(占4字节)。

list.add(obj)实际上是在list底层的数组的对应下标下存储了obj对象的引用,Object prt = list.get(0)也是获取0索引位置上的引用赋值为引用变量prt,list.remove(0)只是在数组中移除了对应对象的引用,而并没有将对象清除,只有在obj对象没有任何引用的情况下才会可能被垃圾回收器回收。

// 例如下面这段代码
List<Interval> intervals = new ArrayList<>(); 
intervals.add(new Interval(10, 20));
intervals.add(new Interval(30, 50));
int count = intervals.size()-1;

Interval next = intervals.get(i);
Interval origin = res.get(count);
res.remove(count); 
// 虽然(30,50)的区间对象在集合中被移除了,但origin对象仍然引用它,这个interval对象就不会被回收。
Interval newInterval = new Interval(origin.start, next.end);

static class Interval {
    int start;
    int end;
}
;