2016-05-14 34 views
1

我創建了一個100萬個MyItem對象的ArrayList和消耗的內存是106MB(從任務管理器檢查)但通過addAll()方法添加到另外兩個列表後,它需要259mb。我的問題是我只添加了對列表的引用,在那100萬之後沒有創建新對象。爲什麼在使用LinkedList的情況下內存消耗會增加(因爲它不需要連續的內存塊,因此不會進行重新分配)?內存分配在100萬集合中的引用在java

如何有效實現這一目標?數據通過我的程序中的各種列表並消耗超過1GB的內存。上面介紹了類似的情況。

public class MyItem{ 
private String s; 
private int id; 
private String search; 

public MyItem(String s, int id) { 
    this.s = s; 
    this.id = id; 
} 

public String getS() { 
    return s; 
} 

public int getId() { 
    return id; 
} 


public String getSearchParameter() { 
    return search; 
} 

public void setSearchParameter(String s) { 
    search = s; 
} 
} 

public class Main{ 
    public static void main(String args[]) { 
     List<MyItem> l = new ArrayList<>(); 
     List<MyItem> list = new LinkedList<>(); 
     List<MyItem> list1 = new LinkedList<>(); 

     for (int i = 0; i < 1000000 ; i++) { 
      MyItem m = new MyItem("hello "+i ,i+1); 
      m.setSearchParameter(m.getS()); 
      l.add(i,m); 
     } 

     list.addAll(l); 

     list1.addAll(l); 
     list1.addAll(list); 

     Scanner s = new Scanner(System.in); 
     s.next();//just not to terminate 
    } 
} 
+0

ArrayList的基礎是它在數組上的名稱。如果數組太小而不能創建新數組,則會創建大小的兩倍,並將引用複製到新數組中。由於Java有它自己的內存管理,所以第一個數組的內存將僅在JVM內部釋放。 – Robert

回答

3

LinkedListdoubly-linked list,所以在th元素列表由節點表示,每個節點包含3個引用。

從Java 8:

private static class Node<E> { 
    E item; 
    Node<E> next; 
    Node<E> prev; 

    Node(Node<E> prev, E element, Node<E> next) { 
     this.item = element; 
     this.next = next; 
     this.prev = prev; 
    } 
} 

由於您使用大量的內存,您可能沒有使用壓縮OOP,所以引用可能是64位,即8個字節的每個。

對於每個引用16個字節+ 8個字節的對象頭,節點佔用40個字節。有100萬個元素,那將是40 Mb。

兩個列表是80 Mb,然後記住Java內存被分割成池和對象(節點)四處移動,而你現在額外消耗153 Mb的內存似乎是正確的。

注意:Arraylist只會使用每個元素8個字節,而不是40個字節,並且如果您預先分配了支持數組,您可以知道大小,您可以通過這種方式節省大量內存。

+0

_因爲你使用了大量的內存,你可能沒有使用壓縮的OOP_,默認情況下,如果堆小於32 GB,UseCompressedOops在64位JVM上啓用,如果它是不是這裏的情況,因爲它只是一個簡單的測試,你不同意嗎? –

3

任何時候你打電話LinkedList.addAll幕後它將爲每個添加元素的LinkedList.Node所以在這裏你創建3百萬這樣的節點這是不是免費的,確實的:

  1. 這個對象有3參考,知道在32-bit JVM和與UseCompressedOops(-XX:+ UseCompressedOops)上的引用的大小是4 bytes,這是默認情況下,Java 7和更高版本中的堆數小於32 GB8 bytes64-bit JVMUseCompressedOops已禁用(-XX:-UseCompressedOops)。所以這裏根據你的配置給出12字節24字節
  2. 然後我們在32-bit JVM16 bytes64-bit JVM上添加標題字段的大小8 bytes。所以在這裏根據你的配置給出8字節16字節

因此,如果我們總結一下它需要:每個實例

  1. 20字節64-bit JVM32-bit JVM
  2. 28字節每個實例有UseCompressedOops啓用
  3. 40字節每個實例在64-bit JVMUseCompressedOops已禁用

您撥打3次,1個億個對象的addAll上一個LinkedList,它給

  1. 60莫32-bit JVM
  2. 84莫64-bit JVMUseCompressedOops啓用
  3. 120 Mo on 64-bit JVMUseCompressedOops殘疾人

其餘的恐怕還沒有被垃圾收集器收集的對象,你應該嘗試加載你的LinkedList後打電話到System.gc()加載你ArrayList才能獲得真正的大小後,做同樣的事情。

如果要獲取給定對象的大小,可以使用SizeOf

如果使用64-bit JVM,你想知道,如果UseCompressedOops啓用,只需在終端啓動您的java命令只-X選項,並增加了-XX:+PrintFlagsFinal | grep UseCompressedOops所以舉例來說,如果我的命令是java -Xms4g -Xmx4g -XX:MaxPermSize=4g -cp <something> <my-class>,推出java -Xms4g -Xmx4g -XX:MaxPermSize=4g -XX:+PrintFlagsFinal | grep UseCompressedOops,的開始輸出應該是這樣的:

 bool UseCompressedOops      := true   {lp64_product} 
    ... 

在這種情況下所述標誌UseCompressedOops啓用

+0

我認爲LinkedList.Node將被分配參考!如果沒有,我怎麼能沒有分配更多的節點工作;(感謝您的回覆! –

+0

這是它的作用,但它創建LinkedList.Node實例爲每個添加的元素保持前一個和下一個節點 –

+0

什麼是確切的Java命令你用於測試嗎? –