參考博主http://blog.csdn.net/xuweilinjijis/article/details/9037635
先看List接口subList方法的javadoc
The returned list is backed by this list, so non-structural
* changes in the returned list are reflected in this list, and vice-versa.
* The returned list supports all of the optional list operations supported
* by this list.
可以看到此方法其實就是直接指向原List集合中的元素,只是改變了開始位置和結束為止而已.如果修改返回對象的數據,那么原對象對應的值也會被修改。
看一下List接口具體實現類 ArrayList類,其中的subList方法源碼如下
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);//檢查是否越界
return new SubList(this, 0, fromIndex, toIndex);//返回截取之后的對象
}
再接着看一下 SubList類的構造器,JDK源碼如下,其實 SubList就是ArrayList中的一個內部類(非靜態的),
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;//引用調用的父集合
private final int parentOffset;//父集合的起始位置
private final int offset;//原對象的起始位置
int size;//現在集合的大小
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;//從此處可以看到父集合一直被子集合引用着,只要子集合存在,父集合就不會被釋放。從而容易造成內存溢出
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
}
下面是一段造成內存溢出的代碼
List<List<Integer>> cache = new ArrayList<List<Integer>>();
try {
while (true) {
ArrayList<Integer> list = new ArrayList<>();
for(int j=0;j<100000;j++) {
list.add(j);
}
List<Integer> subList = list.subList(0, 1);
cache.add(subList);
}
} catch (Exception e) {
}finally {
System.out.println("Cache Size=" + cache.size());
}
運行結果
Exception in thread "main" Cache Size=815
java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.valueOf(Integer.java:832)
at com.effectJava.Chapter2.InstrumentedHashSet.main(InstrumentedHashSet.java:44)
看到只包含815個元素就內存溢出啦。就是因為子對象一只引用着父對象,導致父對象無法回收。從而內存溢出
