【问题标题】:AbstractSpliterator implementation with unknown size throws OutOfMemoryError: Java heap space大小未知的 AbstractSpliterator 实现抛出 OutOfMemoryError: Java heap space
【发布时间】:2017-07-21 17:53:08
【问题描述】:

当我使用AbstractSpliterator 实现时,我得到了一个java.lang.OutOfMemoryError: Java heap space,它报告了一个未知的大小。

在这种情况下,我定义了一个类StreamCollapse,它扩展了AbstractSpliterator,并在tryAdvance() 实现中合并了一系列相邻元素。它的构造函数调用超级构造函数为super(Long.MAX_VALUE, source.characteristics())

关于API documentation 我期待使用Long.MAX_VALUE 表示未知大小。但是,它似乎正在尝试分配具有该大小的内存。

它为什么要分配那个空间?估计大小应该使用什么值?

这是一个示例测试:

Stream<Integer> nrs = Stream.of(3, 3, 5, 5, 3, 3, 3, 4, 4, 4 ,5 , 5);
Integer [] expected = {3, 5, 3, 4, 5};
Object[] actual = collapse(nrs).toArray();
assertEquals(actual, expected);

以及collapse()方法实现:

static <T> Stream<T> collapse(Stream<T> source) {
    return StreamSupport.stream(
            new StreamCollapse<T>(source.spliterator()), false);
}

class StreamCollapse<T> extends AbstractSpliterator<T> implements Consumer<T> {

    private final Spliterator<T> source;
    private T curr = null;

    StreamCollapse(Spliterator<T> source) {
        super(Long.MAX_VALUE, source.characteristics());
        this.source = source;
    }

    @Override
    public boolean tryAdvance(Consumer<? super T> action) {
        T prev = curr;
        boolean hasNext;
        while ((hasNext = source.tryAdvance(this)) && curr.equals(prev)) { }
        if(hasNext) action.accept(curr);
        return hasNext;
    }

    @Override
    public void accept(T item) {
        curr = item;
    }
}

【问题讨论】:

    标签: java-8 java-stream


    【解决方案1】:

    您应该从复合拆分器中删除特征,例如:

    // an unknown spliterator shouldn't having SIZED | SUBSIZED  characteristics
    //                                             v  
    super(Long.MAX_VALUE, source.characteristics() & (~(SIZED | SUBSIZED)));
    

    Spliteartor 是SIZEDSpliteartor,Spliterator#getExactSizeIfKnown 将被流用于创建数组。

    特征值表示在遍历拆分之前从estimateSize()返回的值代表一个有限大小,在没有结构源修改,表示完整遍历将遇到的元素数量的精确计数。

    IF 如果 estimateSize >= Long.MAX_VALUE - 8Stream#toArray 并行运行的流将抛出 IllegalArgumentException

    如果流是顺序流,Stream#toArray 会将其内部数组容量增加到 estimateSize

    【讨论】:

    • 这是正确答案,分离器必须清除SIZEDSUBSIZED特征。另外,使用源拆分器的estimateSize() 作为过滤拆分器的估计大小将是一个好方法。毕竟,实际大小将介于零和源大小之间。 filter 也是这样做的,当前的实现将比完全未知的大小更好地处理这一点。
    • @Holger 感谢您审核我的回答。我也得到你的建议。
    猜你喜欢
    • 1970-01-01
    • 2016-04-15
    • 2015-05-22
    • 1970-01-01
    • 2011-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-16
    相关资源
    最近更新 更多