2013-03-11 43 views
10

將爲ab分配多少個字節?多維數組聲明中的順序是否對已用內存有影響?

import android.graphics.Bitmap; 

Bitmap[][][] a = new Bitmap[1000][2][2]; 
Bitmap[][][] b = new Bitmap[2][2][1000]; 

請注意,我只詢問純數組採用的內存,裏面沒有任何對象。

爲什麼我問?因爲我正在編寫Android遊戲。對我而言,順序無關緊要,但如果存在內存差異,則可以節省一些內存。

回答

10

是的,它確實有所作爲。

在Java中,二維數組是一維數組的數組,而數組(除了所有對象之外)還有頭部,除了保存元素本身所需的空間外。

所以考慮int[10][2]int[2][10],並假設一個32位的JVM。

  • int[2][10]由2個元素的一個數組和2個10個元素的數組組成。總共 - 3個數組對象+ 22個元素。
  • int[10][2]由10個元素的一個數組和10個2元素的數組組成。總共 - 11個數組對象+ 30個元素。

如果我們假設報頭大小是3個32位字(通常用於一個32位JVM)和參考是1個32位字,然後

  • int[2][10]需要3 * 3 + 22 * 1 = 31個字= 124個字節
  • int[10][2]需要11 * 3 + 30×1 = 63個字= 252個字節

應用相同的邏輯和可以用維數較高的估計的陣列的大小。

但很顯然,如果最大尺寸是最右側的尺寸,則可以使用更少的空間。


我已經做了數學與int陣列,但在32位機器上的intreference佔用相同的字節數。在64位機器上,參考的大小可能與intlong的大小相同,具體取決於JVM選項。標題大小也可能不同......不完全確定...可能依賴於平臺。

我還沒有考慮過持有Bitmap對象本身所需的空間,但它是相同的,但是您組織陣列。

+0

謝謝!我有一個問題。也許最好的保存方式是'Bitmap [] c = new Bitmap [2 * 1000]',後來計算索引如'1000 * i + j'?與'Bitmap [2] [1000]'相比,速度沒有區別嗎? – 2013-03-11 13:39:17

+1

1)是的,儘管它會讓你的代碼更難讀,增量保存也不是很好。 2)一維數組*可能會更快,因爲有更少的數組邊界檢查和更少的抓取。但是,差異很小,不可能有任何差異。 – 2013-03-11 13:43:12

+1

請注意,例如,在64位熱點上,默認情況下引用被壓縮爲4個字節。 – assylias 2013-03-11 13:43:20

-1

沒有內存差異,但是您的數組索引的順序可能在理論上對您的程序速度產生影響 。

您通常在嵌套循環中處理多維數組內容。所以你的數組應該按照你在內部循環中處理相鄰元素的方式進行組織,以便編譯器生成最高效的代碼。我不知道的Java是如何組織的內存,但認爲這是不是不同的C/C++:

int a[10][100]; 
for (i = 0; i < 10; ++i) { 
    for (j = 0; j < 100; ++j) { 
     do_something_with(a[i][j]); 
    } 
} 
+2

請您詳細說明一下嗎? – 2013-03-11 13:10:56

+0

您通常在嵌套循環中處理多維數組內容。所以你的數組應該按照你在內部循環中處理相鄰元素的方式進行組織,以便讓編譯器生成最高效的代碼。我不知道java如何組織內存,但認爲它與C/C++沒有什麼不同: int a [10 [100]; (j = 0; j <100; ++ j) do_soemething_with(a [i] [j]);(i = 0; i <10; ++ i) – user1728219 2013-03-12 14:02:01

3

當嘗試它在熱點(準確的數字可能與你所得到的Dalvik不同,但結論應該是相似),I得到的結果如下:

對象陣列(1000x2x2):76034個字節
對象陣列(2x2x1000):16137個字節

這是符合粗略計算:

[2][2][1000]      
Array #  Header Size Memory Number Total 
1    16  2  24  1  24 
2    16  2  24  2  48 
3    16 1000 4016  4 16,064 

         Grand Total  16,136 


[1000][2][2]      
Array #  Header Size Memory Number Total 
1    16 1000 4016  1 4,016 
2    16  2  24 1000 24,000 
3    16  2  24 2000 48,000 

         Grand Total  76,016 

測試下面的代碼,以-XX:-UseTLAB運行以獲得更準確的結果。

public class TestMemory { 

    private static final int SIZE = 100; 
    private static Runnable r; 
    private static Object o; 

    private static void test(Runnable r, String name, int numberOfObjects) { 
     long mem = Runtime.getRuntime().freeMemory(); 
     r.run(); 
     System.out.println(name + ": " + (mem - Runtime.getRuntime().freeMemory())/numberOfObjects + " bytes"); 
    } 

    public static void main(String[] args) throws Exception { 
     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new Object[1000][2][2];} }; 
     test(r, "Object array (1000x2x2)", SIZE); 

     r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new Object[2][2][1000];} }; 
     test(r, "Object array (2x2x1000)", SIZE); 
    } 
} 
+1

這並不令人驚訝,這不是最優化的?差異是巨大的,許多人都沒有意識到這一點。按[]升序排列的排序數字應該是可行的,不是嗎? – 2013-03-11 13:46:06

+0

@AdamStelmaszczyk我知道這是不同的,但從來沒有計算出差異。這確實很大。我看不出爲什麼它不能被優化,因爲你不能「移動指針」,所以我沒有看到它會有什麼負面影響...... – assylias 2013-03-11 13:47:50

3

是的,它是有差別的。只是-Xmx8M試試這個:

// throws OutOfMemoryError 
public static void main(String[] args) { 
    int[][] a = new int[500000][2]; 
    System.out.println("a.length: '" + (a.length) + "'"); 
} 

// works 
public static void main(String[] args) { 
    int[][] a = new int[2][500000]; 
    System.out.println("a.length: '" + (a.length) + "'"); 
} 

第一個將拋出一個OutOfMemoryError,第二個會傳球。

原因是,第一個版本創建500.000長度爲2的數組,而第二個創建長度爲500.000的2個數組。

Reference

在如C語言,一個二維陣列(或實際上任何多維數組)本質上是一維陣列,明智的指針操作。在Java中,情況並非如此,多維數組實際上是一組嵌套數組。這意味着二維數組的每一行都有一個對象的開銷,因爲它實際上是一個單獨的對象!

相關問題