请分析两段Java代码片段之间的内存占用差异
两段Java代码的内存占用差异分析
咱们先把两段代码的完整逻辑补全(第二段代码的循环部分应该是重复添加同一个内层列表),再拆解它们的内存差异:
代码示例1
ArrayList<ArrayList<Integer>> outerList = new ArrayList<ArrayList<Integer>>(); for(int i=0; i<5000; i++){ outerList.add(new ArrayList<Integer>()); for(int j=0; j<5000; j++){ outerList.get(i).add(1); } }
代码示例2
ArrayList<ArrayList<Integer>> outerList_n = new ArrayList<ArrayList<Integer>>(); ArrayList<Integer> innerList_n = new ArrayList<Integer>(); for(int j=0; j<5000; j++){ innerList_n.add(1); } for(int i=0; i<5000; i++){ outerList_n.add(innerList_n); }
核心内存差异分析
这两段代码的本质区别是重复创建对象 vs 复用同一个对象,具体内存开销差异体现在这几个方面:
1. ArrayList对象的数量差异
- 代码1会创建 5001个ArrayList对象:1个外层列表 + 5000个独立的内层列表(每次循环都
new ArrayList<Integer>)。每个ArrayList对象本身会占用内存:包括对象头、size/modCount等元数据、以及指向内部元素数组的引用。 - 代码2只会创建 2个ArrayList对象:1个外层列表 + 1个内层列表(只初始化一次,之后重复添加它的引用到外层列表)。外层列表里的5000个元素都是指向同一个内层列表的引用,而非新对象。
这直接节省了4999个ArrayList对象的内存开销,这部分差异非常显著。
2. 内部元素数组的内存差异
每个ArrayList内部都有一个elementData数组用于存储元素:
- 代码1中,5000个内层列表各有自己的
elementData数组,每个数组需要容纳5000个Integer引用(虽然1会被Integer缓存复用,所以元素本身是同一个对象,但数组是独立的)。每个数组的长度会扩容到至少5000,所以总共有5000个独立的数组占用内存。 - 代码2中,只有1个
elementData数组存储5000个Integer引用,这部分直接节省了4999个数组的内存开销。
3. 额外注意点
代码2的逻辑存在一个隐性的逻辑差异:因为所有外层列表的元素都指向同一个内层列表,所以如果修改任意一个外层元素对应的内层列表(比如outerList_n.get(0).add(2)),所有其他外层元素对应的列表都会同步变化——这是复用对象带来的逻辑副作用,但从纯内存角度看,它的内存占用远低于代码1。
内容的提问来源于stack exchange,提问作者Aadhirai R
相关产品推荐
相关产品推荐

