2017-08-07 44 views
-2

這段代碼爲什麼拋出異常?爲什麼這個hashmap初始容量試圖調整大小?

public static void main(String[] args) { 
    Map<Integer, Integer> map = new HashMap<>(Integer.MAX_VALUE); 
    System.out.println("map size: "+map.size()); 
    map.put(1, 1); 
    System.out.println("map size: "+map.size()); 
} 

輸出:

map size: 0 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
    at java.util.HashMap.resize(HashMap.java:703) 
    at java.util.HashMap.putVal(HashMap.java:628) 
    at java.util.HashMap.put(HashMap.java:611) 
    at com.fredcrs.codejam.NumberToBinary.main(NumberToBinary.java:24) 

不應該HashMap中只調整到一個更大的,當它是滿了嗎?

編輯: 它還與初始化時,它引發同一異常:

Map<Integer, Integer> map = new HashMap<>(Integer.MAX_VALUE-3); 
+0

但是你告訴它有一個'MAX_VALUE的initialCapacity'看到[javadoc文檔(https://docs.oracle.com/javase/7:當我和-Xmx9G運行它成功地與輸出完畢/文檔/ API/JAVA/UTIL/HashMap中。html#HashMap(int)) –

+0

是的,但初始容量意味着用於索引鍵的數組大小(散列) – fredcrs

回答

3
new HashMap<>(Integer.MAX_VALUE); 

您所要求的2個 -1元素,或2147483647初始陣列大小。每個元素8個字節(一個引用是64位),大約16 GB的內存。

除非您有18GB左右的堆可用,否則您將始終得到OOM錯誤。

你問了16GB的陣列內存,除非內存可用,否則它會失敗。實例化還是第一次插入失敗是實現細節。在過去的某個時刻,它會在實例化時失敗。最近代碼被更改爲等待第一次插入。這種改變是可能的,因爲數組分配的細節不是任何外部協議的一部分 - 即JavaDoc中沒有提及。

+0

好吧,我的猜測是當我創建hashmap對象時,它不分配數組.. 。只有當每次添加第一個元素 – fredcrs

+1

時,一個元素被「put」時需要檢查容量並且可能增加,所以我猜測作者解碼後不會分配內存 –

+2

@fredcrs - 'HashMap'用於分配數組最初,但現在它被分配在第一次插入,這是一個非常合理和強大的優化,當你有很多空地圖。 – BeeOnRope

1

在Oracle Java 8 JDK中,只有在添加了元素之後纔會分配HashMap的存儲空間。

如果有疑問,只需檢查實現 - 您甚至可以在調試器中單步執行。

現代JDK HashMap實現在插入第一個元素之前並不實際分配基礎數組,即使您指定了顯式大小。例如,在我的JDK 8版本中,構造函數代碼如下:

public HashMap(int initialCapacity, float loadFactor) { 
    if (initialCapacity < 0) 
     throw new IllegalArgumentException("Illegal initial capacity: " + 
              initialCapacity); 
    if (initialCapacity > MAXIMUM_CAPACITY) 
     initialCapacity = MAXIMUM_CAPACITY; 
    if (loadFactor <= 0 || Float.isNaN(loadFactor)) 
     throw new IllegalArgumentException("Illegal load factor: " + 
              loadFactor); 
    this.loadFactor = loadFactor; 
    this.threshold = tableSizeFor(initialCapacity); 
} 

請注意,沒有分配數組。另外,您請求的尺寸大於我係統上的MAXIMUM_CAPACITY,即2 ,因此實際請求的尺寸(存儲在this.threshold中的爲described here)取決於MAXIMUM_CAPACITY

然後,當你真的去分配數組時,實現會嘗試創建一個所需大小的數組。最終,內心深處HashMap.resize()有一些檢測你已經達到「最大容量」邏輯(因爲你要求的最大容量的初始大小開始與),並且將底層陣列來Integer.MAX_VALUE大小:

if (newThr == 0) { 
     float ft = (float)newCap * loadFactor; 
     newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 
        (int)ft : Integer.MAX_VALUE); 
    } 

然後隨後分配一個數組,其中需要至少8G的堆空間。這就是爲什麼你有OOME。

map size: 0 
map size: 1 
+1

在j8中ArrayList的分配也變得懶惰;比較https://stackoverflow.com/questions/34250207/in-java-8-why-is-the-default-capacity-of-arraylist-now-zero –

相關問題