2014-06-20 100 views
3

我目前正在看關於Android的代碼優化(https://www.youtube.com/watch?v=w9taB0yUwjs對象創建優化

在這段視頻中的視頻,他是優化下面的代碼:

List<Contact> contacts = new ArrayList<Contact>(); 

if (cursor.moveToFirst()) { 
    do { 
     Contact contact = new Contact(...); 
     contacts.add(contact); 
    while(cursor.moveToNext()); 
} 

他認爲,以下將釋放內存。

List<Contact> contacts = new ArrayList<Contact>(); 

if (cursor.moveToFirst()) { 
    do { 
     contacts.add(new Contact(...)); 
    while(cursor.moveToNext()); 
} 

我不太明白爲什麼這會釋放內存。我(有限)的理解是,contact變量只是存儲在堆棧上的對象引用。以匿名方式創建對象實際上是否會顯着減少內存使用量?從閱讀this answer看來,對象引用僅佔用4到8個字節。

我在這裏錯過了什麼嗎?

+1

我不認爲這可以稱爲優化,但只是減少代碼。 –

+0

我認爲你是對的。我看不出這會如何幫助內存使用。 'contact'在本地被定義在循環中,所以變量引用將被拋棄並且對象保持在兩種情況下。 – azurefrog

+0

正如你所指出的,一個對象引用需要4或8個字節,但是你看今天的電子設備是微不足道的。在這種情況下,製作視頻的人不知道他在說什麼。 – awksp

回答

3

這可能過於簡單了,但它會給你基本的想法。

這是一個測試類比較的方法有一個額外的參考和等效的方法與內聯調用:

public class Test { 
    static List<String> list = new ArrayList<>(); 

    public static void extraReference() { 
     String s = new String(); 
     list.add(s); 
    } 

    public static void noReference() { 
     list.add(new String()); 
    } 
} 

下面是該方法的字節碼,以相同的順序,他們宣稱:

public static void extraReference(); 
    Code: 
     0: new   #2     // class java/lang/String 
     3: dup   
     4: invokespecial #3     // Method java/lang/String."<init>":()V 
     7: astore_0  
     8: getstatic  #4     // Field list:Ljava/util/List; 
     11: aload_0  
     12: invokeinterface #5, 2   // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 
     17: pop   
     18: return   

    public static void noReference(); 
    Code: 
     0: getstatic  #4     // Field list:Ljava/util/List; 
     3: new   #2     // class java/lang/String 
     6: dup   
     7: invokespecial #3     // Method java/lang/String."<init>":()V 
     10: invokeinterface #5, 2   // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 
     15: pop   
     16: return 

如果你仔細看,只有區別是字節碼中的額外引用存儲/加載指令。

現在,如果此代碼是按原樣執行的,則可能會在很多調用之後發現差異 - 例如在循環中。額外的CPU週期可能會被燒燬,並且您必須在堆棧中使用一個位置來存儲引用(由於GC只處理堆,所以根本不應該打擾GC,並且堆棧中的項自動釋放this答案)。但我不會撥打成本顯着

然而,實際上在每個JVM(以及Android使用的Dalvik VM,如果內存服務)上都存在被稱爲JIT編譯器的神奇實體。 JIT編譯器具有內嵌額外引用的能力,因此具有額外引用的代碼基本上與沒有額外引用的代碼完全相同。這應該是JIT編譯器執行相對容易的優化,特別是對於更現代的VM。

所以最後,如果有差異,你可以放心地忽略它由於JITC。在這種情況下,您應該選擇更易讀的代碼風格。

+0

複製,感謝,迄今爲止最深入的解釋。我實際上正在閱讀[關於Dalvik JIT編譯器](http://android-developers.blogspot.co.uk/2010/05/dalvik-jit.html) – user184994

+0

@ user184994沒問題!有關如何處理堆棧上的項目的參考文件的小版本更新,但否則應該很好。我也會查看那篇文章,因爲我很好奇JIT編譯器可以在沒有臺式機具有的資源的情況下工作多少。 – awksp

0

隨着這種改變,代碼看起來更「優化」了一些,但沒有它不會顯着減少內存使用。

1

在他提到擺脫對象的引用的視頻,從

if (cursor.moveToFirst()) { 
    do { 
     Contact contact = new Contact(...); 
     contacts.add(contact); 
    while(cursor.moveToNext()); 
} 

更改爲

Contact contact = null; 
if (cursor.moveToFirst()) { 
    do { 
     contact = new Contact(...); 
     contacts.add(contact); 
    while(cursor.moveToNext()); 
} 

然後

if (cursor.moveToFirst()) { 
    do { 
     contacts.add(new Contact(...)); 
    while(cursor.moveToNext()); 
} 

在任何情況下,我認爲他的意思是(我不能確定)這是對可讀性的改進,因爲內存將在creati上消耗的對象,而不是變量分配。

+0

如果cursor.moveToFirst()返回true,則只需要聯繫人聯繫人。 – JustinDanielson

+0

不是我的代碼:)。剛剛從視頻 –

0

優化也可能發生在編譯器端,而不是通過閱讀代碼來揭示。 任何形式的代碼在這裏都不會改變對象的生命週期,所以對於我來說沒有優化,只需要更少的代碼。