前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ArrayList源码分析(基于jdk1.8)(二):subList陷阱补充

ArrayList源码分析(基于jdk1.8)(二):subList陷阱补充

作者头像
冬天里的懒猫
发布2020-08-11 14:48:32
4650
发布2020-08-11 14:48:32
举报

关于ArrayList的subList方法,还会出现另外一个问题就是强引用释放问题。看如下案例:

public static void main(String[] args) {

		List<List<Integer>> data = new ArrayList<>();

		for(int i=0;i<100000;i++) {
			List<Integer> list = IntStream.rangeClosed(1,100000).boxed().collect(Collectors.toList());
			data.add(list.subList(0,10));
		}
		
	}

在设置jvm参数为 -Xms100M -Xmx100M的情况下,运行一段时间之后:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.lang.Integer.valueOf(Integer.java:832)
	at java.util.stream.IntPipeline$$Lambda$1/990368553.apply(Unknown Source)
	at java.util.stream.IntPipeline$4$1.accept(IntPipeline.java:250)
	at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
	at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:693)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
	at com.dhb.ArrayList.test.SubListOOM.main(SubListOOM.java:15)

没错,会出现OOM问题。实际上在读过ArrayList源码分析(基于jdk1.8)(二):subList陷阱之后,就能想到这个问题。这也是最近在阅读极客时间的时候,正好也发现了这个问题就一并总结。

前文提到,如果采用subList则实际上是返回的一个内部类SubList。

subList方法 :

public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}

SubList源码:

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;
    }
}

可以看到,在subList方法调用的时候将this传递到了SubList的parent属性中。这是一个强引用。那么在GC的过程中,这是不会被回收的。如果我们在实际的编码过程中对SubList不了解,误将subList当成了一个全新的ArrayList,这就有可能因为GC无法回收之前的原始List而导致OOM异常。

结合前文的知识,我们只需要稍加改动,虽然执行时间很长,但是就就不会出现OOM了。

	public static void main(String[] args) {

		List<List<Integer>> data = new ArrayList<>();

		for(int i=0;i<100000;i++) {
			List<Integer> list = IntStream.rangeClosed(1,100000).boxed().collect(Collectors.toList());
			data.add(new ArrayList<>(list.subList(0,10)));
		}

	}

因此copyOnRead或CopyOnWrite这是一个非常重要的设计模式,在后面会专门进行分析。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-08-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档