Java:ArrayList如何管理内存

Java:ArrayList如何管理内存,java,memory,arraylist,Java,Memory,Arraylist,在我的Data Structures类中,我们研究了Java ArrayList类,以及当用户添加更多元素时它如何增长底层数组。这是可以理解的。但是,当从列表中删除大量元素时,我无法弄清楚这个类究竟是如何释放内存的。从源代码看,有三种方法可以删除元素: public E remove(int index) { RangeCheck(index); modCount++; E oldValue = (E) elementData[index]; int numMoved = size

在我的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 < size; index++)
  if (o.equals(elementData[index])) {
      fastRemove(index);
      return true;
  }
        }
 return false;
}


private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index, 
                             numMoved);
        elementData[--size] = null; // Let gc do its work
}
public E-remove(int-index){
范围检查(索引);
modCount++;
E旧值=(E)元素数据[索引];
int numMoved=大小-索引-1;
如果(numMoved>0)
System.arraycopy(elementData,索引+1,elementData,索引,
numMoved);
elementData[--size]=null;//让gc完成它的工作
返回旧值;
}
公共布尔删除(对象o){
如果(o==null){
对于(int index=0;index0)
System.arraycopy(elementData,索引+1,elementData,索引,
numMoved);
elementData[--size]=null;//让gc完成它的工作
}

它们都没有减少数据存储阵列。我甚至开始质疑是否曾经出现过内存释放,但实证测试表明确实如此。所以一定有其他的方法,但是在哪里,如何?我也检查了父类,但没有成功。

它们不会减少底层数组。他们只是缩小尺寸。其原因是,如果一个数组中有1000个元素并删除了1,为什么要重新分配和复制该数组?这是极大的浪费,收获甚微

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

  • 大小:列表中概念上有多少元素;及

  • 容量:基础阵列中可以容纳多少个元素


ArrayList
扩展时,即使只添加一个元素,它的大小也会增长约50%。相反,这是一个类似的原则。基本上可以归结为:重新分配数组和复制值是(相对)昂贵的。如此之多,以至于你想尽量减少它的发生。只要概念大小的工厂大约是数组大小的2,就不值得担心。

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

调整ArrayList内部数组的大小并没有多大好处,即使您使用ArrayList来容纳一个大对象

List<LargeObject> list = new ArrayList<LargetObject>();
List List=new ArrayList();
列表将只保存对LargeObject实例的引用,而不保存LargeObject实例本身


引用不会占用太多空间。(将其视为C中的指针)

数组大小永远不会自动减小。实际上,很少有一个列表首先填充了大量元素,然后清空了,但仍然保留着。请记住,必须有足够的内存来保存列表(仅由引用组成)及其元素,因此空列表消耗的内存不太可能成为问题


如果您真的遇到了一个非常奇怪的算法,导致出现问题,您仍然可以通过手动调用来释放内存。

据我所知,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();
ArrayList al=new ArrayList();
//为了演示,请填写列表
对于(int i=0;i<1000000;++i)
{
al.添加(i);
}
//现在删除除一个元素以外的所有元素
al.removeRange(1,al.size());
//这将使阵列缩小到合适的大小
al.trimToSize();

注意,在GC再次运行之前,可用内存量可能不会改变。

请重新格式化代码。Stackoverflow不理解[代码],请编辑您的文章,选择代码段,然后按“代码”按钮对其进行格式化。是的,这是我的第一篇文章,我正在研究如何使用格式化。不过有人帮了我:)是的,我明白在删除一个元素后重新分配是愚蠢的。但是,如果我添加一百万个元素,然后删除所有元素,只删除一个,并且在程序结束之前不添加任何内容,会怎么样。保留一个百万单元格数组而不是默认大小将是一种内存浪费。@cka3o4nik:这只是一个引用数组。一百万个阵列仍然只占用大约4MB的空间——这并不值得担心如此罕见的情况。@cka304nik正如Michael所说,减少阵列大小并不是一个大问题。但是,如果您确实想这样做,您可以调用
ArrayList.trimToSize()
。感谢您的解释。我最终将ArrayList源代码复制到了我的测试项目中,并添加了一个getCapacity()方法来访问基础阵列。正如你们所预测的,除非我通过trimToSize()手动请求,否则它永远不会下降。谢谢。奇怪的是,人们对C语言中的指针如此恐惧,而对Java中的引用思想却如此满意。