ArrayList的层底是一个Object数组,当执行remove方法的时候,会把对应的元素从后往前移动一位,实现删除功能,代码以下:数组
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
但在下面有一句很是关键的代码性能
elementData[--size] = null; // clear to let GC do its work
注释也说明了,将最后一位元素指定为null,让GC来回收,而这一位元素,能够称为过时对象,由于你的size已经执行--操做,你放在数组的最后一位,永远也不会再被使用,若是你不指定用null来进行回收,随着垃圾回收器活动的增长,或者因为不断的增长内存占用,程序性能的下降会逐渐的表现出来,在极端的状况,会出现内存泄露,固然这种状况确实少见。测试
下面再用一个进栈出栈的小例子来详细的解释一个过时对象的引用处理this
public class Stack { private Object[] elements; private int size = 0; public Stack(int capacity) { this.elements = new Object[capacity]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity() { if (elements.length == size) { Object[] oldElements = elements; elements = new Object[2 * elements.length + 1]; System.arraycopy(oldElements, 0, elements, 0, size); } } }
这是一段简单的进栈出栈代码,在pop()方法中,直接取最后一位元素,同时,记录数组的size也减了一,这时,一样如ArrayList同样,最后一位元素在方法中不再会被引用,成为过时对象。若是不考虑GC的问题,上段代码你怎么测试,都会正常执行,可是咱们不该该让本身的程序存在这样一个潜在的GC问题,应在POP方法中,以下解决code
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; return result; }
ArrayList的源码和上述的例子,都是由于若是不置为null, 栈内部维护着对这些对象的过时引用(absolete reference)。所谓的过时引用,是指永远也不会再被解禁的引用。凡是在elements数组的“活动区域(active portion)”以外的引用都是过时的,elements的活动区域是指下标小于size的那一部分。对象
Java内存泄漏是很隐蔽的,若是一个对象引用被无心识地人口负债期起来,那么GC不只不会处理这个对象,并且也不会处理被这个对象引用的全部其它对象,即便只有少许的几个对象引用被无心识地保留下来,也会有不少的对象被排除在垃圾回收机制以外,从而对性能形成潜在的重大影响。内存