以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;
}