1. ホーム
  2. java

[解決済み] Java 8 のコンストラクタ参照で恐ろしいパフォーマンスと大きなヒープフットプリント?

2022-10-28 16:57:54

質問

本番環境において、かなり不愉快な経験をしました。 OutOfMemoryErrors: heapspace..

この問題の原因をたどると、私が使っている ArrayList::new を関数で使用したことに起因します。

これが実際に宣言されたコンストラクタを介した通常の作成よりも悪いパフォーマンスであることを検証するために、( t -> new ArrayList<>() ) を介しての通常の作成よりも実際にパフォーマンスが悪いことを確認するために、次のような小さなメソッドを書きました。

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

このメソッドを newMethod=true; で実行すると、このメソッドは OutOfMemoryError で失敗します。この場合 newMethod=false; では、プログラムは失敗せず、殺されるまで叩き続けます (インデックスは簡単に 150 万に達します)。

なぜ ArrayList::new がこんなにたくさん Object[] 要素がヒープ上に生成され、それが OutOfMemoryError を発生させるほど速いのですか?

(ちなみに - コレクションの型が HashSet .)

どのように解決するのですか?

最初のケース ( ArrayList::new ) では のコンストラクタを使用しています。 を使用している場合、2番目のケースはそうではありません。大きな初期容量 ( index を指定した場合) は、大きな Object[] が確保され、その結果 OutOfMemoryError s.

以下は2つのコンストラクタの現在の実装です。

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 が呼び出されるまで配列は割り当てられません。