首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >可怕的性能& Java 8构造函数引用的大堆占用量?

可怕的性能& Java 8构造函数引用的大堆占用量?
EN

Stack Overflow用户
提问于 2016-02-09 16:10:14
回答 2查看 4.3K关注 0票数 111

我只是在我们的生产环境中有一个相当不愉快的经历,导致了OutOfMemoryErrors: heapspace..

我跟踪这个问题是因为我在一个函数中使用了ArrayList::new

为了验证通过声明的构造函数(t -> new ArrayList<>())执行比正常创建更糟糕的操作,我编写了以下小方法:

代码语言:javascript
复制
public class TestMain {
  public static void main(String[] args) {
    boolean newMethod = false;
    Map<Integer,List<Integer>> map = new HashMap<>();
    int index = 0;

    while(true){
      if (newMethod) {
        map.computeIfAbsent(index, ArrayList::new).add(index);
     } else {
        map.computeIfAbsent(index, i->new ArrayList<>()).add(index);
      }
      if (index++ % 100 == 0) {
        System.out.println("Reached index "+index);
      }
    }
  }
}

在索引达到30k后,使用newMethod=true;运行该方法将导致OutOfMemoryError失败。使用newMethod=false;,该程序不会失败,但会一直执行到死亡(索引很容易达到1.5百万)。

为什么ArrayList::new会在堆中创建这么多的Object[]元素,以至于导致OutOfMemoryError的速度如此之快?

(顺便说一句,当集合类型为HashSet时也会发生这种情况。)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-02-09 16:21:33

在第一种情况(ArrayList::new)中,您使用的是接受初始容量参数的constructor,而在第二种情况下,则不是。较大的初始容量(代码中的index)会导致分配大量的Object[],从而导致OutOfMemoryError

下面是两个构造函数的当前实现:

代码语言:javascript
复制
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

HashSet中也会发生类似的情况,除非在调用add之前不会分配数组。

票数 99
EN

Stack Overflow用户

发布于 2016-02-09 16:32:23

computeIfAbsent签名如下:

代码语言:javascript
复制
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

因此,mappingFunction是接收一个参数的函数。在您的例子中,K = IntegerV = List<Integer>,因此签名变成(省略PECS):

代码语言:javascript
复制
Function<Integer, List<Integer>> mappingFunction

当您在需要使用ArrayList::new的地方编写Function<Integer, List<Integer>>时,编译器会查找合适的构造函数,即:

代码语言:javascript
复制
public ArrayList(int initialCapacity)

所以从本质上说,您的代码相当于

代码语言:javascript
复制
map.computeIfAbsent(index, i->new ArrayList<>(i)).add(index);

您的键被视为initialCapacity值,这会导致不断增加的数组的预分配,这当然会很快导致OutOfMemoryError

在这种情况下,构造函数引用不合适。用兰巴斯代替。如果Supplier<? extends V>computeIfAbsent中使用,那么ArrayList::new是合适的。

票数 81
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35296734

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档