Android性能優(yōu)化:編碼小細(xì)節(jié)提升性能

正確使用單例

說到單例,一般我們都會想到懶漢和惡漢模式。如下惡漢單例模式如下:

public class Singleton {  
      private static Singleton instance = new Singleton();  
      private Singleton (){}

      public static Singleton getInstance() {  
          return instance
      }
  }

惡漢模式不存在線程同步的問題,因為instance的實例在虛擬機加載Singleton類的時候就被初始化了。但是這里不足之處就是不能等待要用的時候再加載,既懶加載,因此就會在不使用的時候需要占用一定的內(nèi)存空間,類的加載機制經(jīng)過加載、鏈接(驗證、準(zhǔn)備、解析)、初始化三個階段,啟中再鏈接的準(zhǔn)備階段會對靜態(tài)成員分配一定的內(nèi)存空間,并為其設(shè)置初始值,然后初始化階段會對類中static關(guān)鍵字的代碼按順序執(zhí)行一遍,static靜態(tài)成員變量會對準(zhǔn)備階段的初始值進行覆蓋。

而懶漢模式如下,其更是存在性能上的缺陷,或者因為使用不當(dāng)使得代碼存在潛在的bug。

示例1:

 public class Singleton {  
      private static Singleton instance;  
      private Singleton (){}

      public static synchronized Singleton getInstance() {  
      if (instance == null) {  
          instance = new Singleton();  
      }  
      return instance; 
      }
  }

懶漢模式因為加上synchronized關(guān)鍵字避免線程同步的問題,但同時也增加了性能開銷,100萬次調(diào)用getInstance情況下,單個線程加了synchronized的時間開銷為106ms,而沒加的開銷為72ms。若是10個線程,加了synchronized的開銷4436ms,沒加synchronized為192ms。因此可以看出synchronized對性能是有一定的影響的。

示例1代碼還存在代碼缺陷,若A和B兩個線程同時調(diào)用到方法,A先搶到資源,因此進入到if語句,判斷為空進行了初始化,然后釋放資源之后,B搶的資源也會判斷為空,因此造成創(chuàng)建了多個實例。

示例2:

 public class Singleton {  
      private static Singleton instance;  
      private Singleton (){}

      public static Singleton getInstance() {  
      if (instance == null) {  
          synchonized(Singleton.class) {
              if (instance == null) {
                  instance = new Singleton();  
              }
          }
      }  
      return instance; 
      }
  }

這種情況看似解決示例1重復(fù)創(chuàng)建實例的問題,但是仍然存在一個指令重排的問題。JVM內(nèi)部會對指令進行重排,如下當(dāng)執(zhí)行instance = new Singleton(); JVM轉(zhuǎn)換成多條指令如下:

memory = allocate(); //1:分配對象的內(nèi)存空間
ctorInstance(memory); //2:初始化對象
instance = memory; //3:設(shè)置instance指向剛分配的內(nèi)存地址

但經(jīng)過重排的指令可能變成如下:

memory = allocate(); //1:分配對象的內(nèi)存空間
instance = memory;  //3:設(shè)置instance指向剛分配的內(nèi)存地址,此時對象還沒被初始化
ctorInstance(memory); //2:初始化對象

因此就會存在一個問題,當(dāng)線程A還沒初始化對象之前,線程調(diào)用getInstance的時候,就有可能存在instance不為空的情況,但此時因為還沒初始化,接著線程嘗試調(diào)用Singleton對象方法的時候就存在錯誤的情況。因此,在定義Singleton的instance前需要加上volatile修飾符。通過volatile可以對各個線程對某個變量可見性,某個變量修改了,其他線程是立即可見的,因為編譯指令之后,會把volatile操作設(shè)置成內(nèi)存屏障(Memory Barrier),volatile保證了時序,但不是原子性的,不能起到同步關(guān)鍵字的作用,因此示例2的synchonized關(guān)鍵字仍然需要。

示例3:使用靜態(tài)內(nèi)部類實現(xiàn)單例

public class Singleton {   
      private Singleton (){}

      public static Singleton getInstance() {
        return InnerInstance.instance;
      }
      
      public static class InnerInstance {
          private static Singleton instance = new Singleton();
      }
  }

示例3使用靜態(tài)類,在類被調(diào)用的時候才由虛擬機進行類加載,因此既可以實現(xiàn)同步,也失效了懶加載。

示例4:使用枚舉類實現(xiàn)單例

public enum Singleton {
    INASTANCE;
    public Singleton getInstance() {
        return INASTANCE;
    }
}

使用枚舉實現(xiàn)單例,可以避免被反射串改等,更重要的,可以避免序列化問題,既序列化和反序列化后可以仍然為同一個實例。

是否有必要使用Enum枚舉

我們在枚舉一些常亮的時候經(jīng)常使用枚舉,枚舉在使用上是線性安全的,在加載的時候虛擬機可以保證線性安全、枚舉可以有自己的方法。然而,枚舉在Android系統(tǒng)也有它不足的地方,使用枚舉會增加dex的大小以及方法數(shù)使用枚舉比使用常量會曾加更多內(nèi)存,更重要的是,因為枚舉在使用的時候加載枚舉類,會實例化所有的枚舉類并且是靜態(tài)的,這些枚舉的實例是單例的,不會回收,如果沒有注意銷毀的話,可能就存在內(nèi)存泄露的問題。另外,在編譯的時候,枚舉會產(chǎn)生常量,在調(diào)用的時候會增加額外的方法調(diào)用。這些都是不利性能的。因此,無論是對文件大小、方法調(diào)用、以及內(nèi)存使用上,枚舉都存在一定的弊端,我們使用的時候要注意這一塊的處理。

因此,建議使用intDef、stringDef來代替枚舉,typeDef既可以像Enum進行枚舉,但不會有Enum枚舉類存在的性能弊端,且方法的調(diào)用可以相對定義的靜態(tài)常量提高編譯限制。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本系列出于AWeiLoveAndroid的分享,在此感謝,再結(jié)合自身經(jīng)驗查漏補缺,完善答案。以成系統(tǒng)。 Java基...
    濟公大將閱讀 1,611評論 1 6
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,011評論 0 11
  • 所有知識點已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,706評論 1 4
  • 1.面向?qū)ο蟮奶卣饔心男┓矫妫?抽象:抽象是將一類對象的共同特征總結(jié)出來構(gòu)造類的過程,包括數(shù)據(jù)抽象和行為抽象兩方面...
    浪花易逝閱讀 713評論 0 5
  • 在洶涌的車潮中,我努力在手機上找到了Tom Waits。沙啞的聲音流出來,我開始在腦子里勾畫Waits 的形象,年...
    路路英來合作小書集閱讀 203評論 0 0

友情鏈接更多精彩內(nèi)容