Java:ArrayList如何管理内存

在我的Data Structures类中,我们研究了Java ArrayList类,以及当用户添加更多元素时它如何增长底层数组。 这是明白的。 但是,当从列表中删除大量元素时,我无法弄清楚这个类究竟是如何释放内存的。 查看源代码,有三种方法可以删除元素:

public E remove(int index) { RangeCheck(index); modCount++; E oldValue = (E) elementData[index]; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work return oldValue; } public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index  0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work } 

它们都不会减少数据存储arrays。 我甚至开始质疑是否会发生内存释放,但实证测试表明它确实存在。 所以必须有其他方式,但在哪里以及如何? 我检查了父类,但没有成功。

它们不会减少底层数组。 它们只是减小了尺寸。 这样做的原因是,如果数组中有1000个元素并删除1,为什么要重新分配和复制数组? 只需很少的收益就非常浪费。

基本上Java ArrayList有两个重要的属性,重要的是要理解它们是不同的:

  • size: List中的元素数量; 和

  • capacity:底层数组中可以容纳多少个元素。

ArrayList扩展时,即使您只添加一个元素,它也会增加大约50%的大小。 这是一个相反的原理。 基本上它归结为:重新分配数组并复制值是(相对)昂贵的。 这么多,所以你想尽量减少它的发生。 只要名义尺寸的arrays大小约为2的工厂,就不值得担心。

据我所知,ArrayList不会自动收缩。 但是,您可以这样说:

 ArrayList al = new ArrayList(); // fill the list for demo's sake for (int i = 0; i < 1000000; ++i) { al.add(i); } // now remove all but one element al.removeRange(1, al.size()); // this should shrink the array back to a decent size al.trimToSize(); 

请注意,可用内存量可能不会更改,直到GC再次运行。

我必须再看一下ArrayList的源代码,但是remove会从数组remove删除该对象,然后如果该对象没有被任何其他对象引用,则GC可以删除该对象。 但arrays大小并未减少。

通过调整ArrayList内部数组的大小,即使您使用ArrayList来保存大对象也没有太大的好处。

 List list = new ArrayList(); 

list只保存对LargeObject实例的引用,而不保存LargeObject实例本身。

参考不会占用太多空间。 (把它想象成C中的指针)

数组大小永远不会自动减少。 实际上有一个列表首先填充了大量的元素,然后将其清空但仍然保持不变,这是非常罕见的。 请记住,必须有足够的内存来保存列表(仅包含引用)及其元素 – 然后空列表消耗的内存不太可能成为问题。

如果你真的遇到一个奇怪的算法,这会成为一个问题,你仍然可以通过手动调用trimToSize()来释放内存。